Skip to content

Comments

Fix inconsistent ISO timestamp formatting causing ValueError on step update#2798

Open
hztBUAA wants to merge 3 commits intoChainlit:mainfrom
hztBUAA:fix/timestamp-parsing-2491
Open

Fix inconsistent ISO timestamp formatting causing ValueError on step update#2798
hztBUAA wants to merge 3 commits intoChainlit:mainfrom
hztBUAA:fix/timestamp-parsing-2491

Conversation

@hztBUAA
Copy link
Contributor

@hztBUAA hztBUAA commented Feb 20, 2026

Summary

Fixes #2491

ChainlitDataLayer saves timestamps with a trailing Z (e.g., 2025-09-04T02:00:42.164000Z) via ISO_FORMAT, but reads them back using Python's .isoformat() which omits the Z (e.g., 2025-09-04T02:00:42.164000). When update_step calls create_step with the read-back createdAt string, strptime fails with:

ValueError: time data '2025-09-04T02:00:42.164000' does not match format '%Y-%m-%dT%H:%M:%S.%fZ'

Changes

  • Add _parse_iso_datetime() helper that tolerates both with and without trailing Z
  • Add _datetime_to_utc_iso() helper that ensures all datetime-to-string conversions consistently include the trailing Z
  • Replace all bare .isoformat() calls with _datetime_to_utc_iso() across user, thread, and step serialization
  • Replace datetime.strptime(created_at, ISO_FORMAT) with _parse_iso_datetime(created_at) in create_step
  • Switch datetime.now() to datetime.utcnow() in get_current_timestamp() and update_thread() for correct UTC semantics
  • Add comprehensive tests covering parse/format roundtrip and step row conversion

Test plan

  • _parse_iso_datetime correctly parses both "...Z" and "..." formats
  • _datetime_to_utc_iso always produces strings ending with Z
  • Step row conversion produces Z-suffixed timestamps that can be re-parsed by create_step
  • None timestamps are preserved as None (not formatted)
  • Invalid formats still raise ValueError

Summary by cubic

Fix inconsistent ISO timestamp handling to prevent ValueError during step updates (#2491). Standardizes UTC timestamps with a trailing Z and makes parsing accept both formats.

  • Bug Fixes
    • Added tolerant ISO parsing that accepts with or without trailing Z.
    • Added UTC formatter that appends Z and strips timezone offsets; replaced isoformat/strptime in user, thread, and step serialization.
    • Switched datetime.now to datetime.utcnow for current timestamps and thread updates.
    • Added tests for parse/format roundtrip and step timestamp conversion; None timestamps remain None; aligned with ruff and strict pytest rules.

Written for commit ce95553. Summary will update on new commits.

…update

Timestamps were saved with trailing Z (UTC indicator) but read back via
.isoformat() without Z, causing a ValueError when update_step tried to
re-parse the createdAt string via strptime with the Z-requiring format.

Changes:
- Add _parse_iso_datetime() helper that handles both with/without Z
- Add _datetime_to_utc_iso() helper that ensures trailing Z on output
- Replace all .isoformat() calls with _datetime_to_utc_iso()
- Replace strptime(created_at, ISO_FORMAT) with _parse_iso_datetime()
- Switch datetime.now() to datetime.utcnow() for UTC consistency
- Add tests covering parse/format roundtrip and step row conversion

Fixes Chainlit#2491
@dosubot dosubot bot added size:L This PR changes 100-499 lines, ignoring generated files. data layer Pertains to data layers. labels Feb 20, 2026
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 2 files

@dokterbob dokterbob added review-me Ready for review! unit-tests Has unit tests. labels Feb 23, 2026
@dokterbob
Copy link
Collaborator

Would love to merge this, please run uv run ruff format over your code! :)

Copy link
Collaborator

@dokterbob dokterbob left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great contrib! Please make requested fixups and ensure only code related to the problem at hand is in there! (E.g. stuff described in the PR description.)

If more changes are needed, knowing WHY is essential!


def test_parse_without_z_raises_on_bad_format(self):
"""Test that invalid format still raises ValueError."""
with pytest.raises(ValueError):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is actually correct, please add comment telling ruff to ignore this (explaining that it's the exception from underlying code).

mime=str(row["mime"]),
objectKey=str(row["objectKey"]),
mime=str(row["mime"]) if row.get("mime") else None,
objectKey=row.get("objectKey"),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes here seem unrelated to the problem at hand.

"Failed to get read URL for element '%s': %s",
elem.get("id", "unknown"),
e,
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure it's a good idea to just log general Exception. This definitely should not be part of a fix regarding date time conversion!

@hztBUAA
Copy link
Contributor Author

hztBUAA commented Feb 25, 2026

Addressed review feedback in commit de2609ed.

Changes made:

  • Removed unrelated changes in get_element (url/mime/objectKey handling).
  • Removed the broad Exception logging block from get_thread.
  • Added a targeted # noqa: PT011 explanation on the pytest.raises(ValueError) assertion in tests.

The PR is now scoped to the timestamp parsing/formatting fix.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 2 files (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="backend/chainlit/data/chainlit_data_layer.py">

<violation number="1">
P2: get_element now coerces nullable url/mime/objectKey fields to strings, turning NULLs into the literal "None" and changing API semantics for consumers expecting None/falsy values.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@@ -1,4 +1,5 @@
import json
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: get_element now coerces nullable url/mime/objectKey fields to strings, turning NULLs into the literal "None" and changing API semantics for consumers expecting None/falsy values.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend/chainlit/data/chainlit_data_layer.py, line 299:

<comment>get_element now coerces nullable url/mime/objectKey fields to strings, turning NULLs into the literal "None" and changing API semantics for consumers expecting None/falsy values.</comment>

<file context>
@@ -296,10 +296,10 @@ async def get_element(
             threadId=str(row["threadId"]),
             type=metadata.get("type", "file"),
-            url=row.get("url"),
+            url=str(row["url"]),
             name=str(row["name"]),
-            mime=str(row["mime"]) if row.get("mime") else None,
</file context>
Fix with Cubic

@hztBUAA
Copy link
Contributor Author

hztBUAA commented Feb 25, 2026

Follow-up after running full local verification in a project-specific environment.

Updated branch head: ce955531

Validation run (from backend/):

  • .venv/bin/python -m ruff format --check chainlit/data/chainlit_data_layer.py tests/data/test_chainlit_data_layer.py
  • .venv/bin/python -m ruff check chainlit/data/chainlit_data_layer.py tests/data/test_chainlit_data_layer.py
  • PYTHONPATH=. .venv/bin/python -m pytest tests/data/test_chainlit_data_layer.py -q

Results:

  • Ruff format: pass
  • Ruff lint: pass
  • Pytest: 11 passed (with one existing pydantic deprecation warning)

This PR is now validated end-to-end locally with no pending formatter/lint/test issues on the touched scope.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

data layer Pertains to data layers. review-me Ready for review! size:L This PR changes 100-499 lines, ignoring generated files. unit-tests Has unit tests.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Time are messed up in Chainlit

3 participants