Skip to content

fix(sessions): handle SQLite float timestamps in PreciseTimestamp.result_processor#6355

Open
abhiramArise wants to merge 6 commits into
google:mainfrom
abhiramArise:fix/database-session-timestamp-float
Open

fix(sessions): handle SQLite float timestamps in PreciseTimestamp.result_processor#6355
abhiramArise wants to merge 6 commits into
google:mainfrom
abhiramArise:fix/database-session-timestamp-float

Conversation

@abhiramArise

@abhiramArise abhiramArise commented Jul 10, 2026

Copy link
Copy Markdown

Fixes #6352

Link to Issue or Description of Change

1. Link to an existing issue (if applicable):

2. Or, if no issue exists, describe the change:

Problem:
DatabaseSessionService.get_session() raised TypeError: fromisoformat: argument must be str when reading rows where the timestamp column held a raw SQLite REAL-affinity float instead of the text format SQLAlchemy's DateTime type normally writes.

Solution:
PreciseTimestamp (a TypeDecorator wrapping SQLAlchemy DateTime) had no override for handling this case. My first attempt used process_result_value, but that hook runs after the underlying impl's own result processor — which already fails trying to parse a float as ISO text before process_result_value can intercept it (thanks to @surajksharma07 for catching this in review). Fixed by overriding result_processor directly instead, which runs before the impl's own processor. It handles int/float values explicitly via datetime.fromtimestamp(), matching the naive local-time convention already used on the write side in StorageEvent.from_event, and delegates to the impl's normal processor for everything else.

Testing Plan

Unit Tests:

  • I have added or updated unit tests for my change.
  • All unit tests pass locally.

Added test_database_session_service_sqlite_file_timestamp_read_after_reopen in tests/unittests/sessions/test_session_service.py. Verified the test is meaningful: confirmed it FAILS with the original TypeError: fromisoformat: argument must be str when the fix is reverted, and PASSES when the fix is in place.

Note: writing through the normal ORM path (append_eventStorageEvent.from_event) doesn't reproduce this bug, since SQLAlchemy's SQLite DateTime type already serializes to text before the value reaches the column. The test writes a row normally, then directly overwrites the timestamp column via raw SQL to force a real float into it (simulating a REAL-affinity value from any write path that bypasses SQLAlchemy's serialization), then confirms get_session() reads it back correctly.

Ran the full session test suite locally:

pytest -q tests/unittests/sessions/test_session_service.py
→ 133 passed, 4 failed (all pre-existing and unrelated to this change, confirmed present on unmodified main as well: a flaky microsecond-timing assertion in test_create_get_session, a Windows-only near-epoch OSError in an unrelated parametrized test, an intermittent test-order state-leakage failure in test_append_event_calls_rollback_on_commit_failure, and a missing optional vertexai dependency in my local environment).

Also ran the full multi-version suite via tox as required by CONTRIBUTING.md:
I was unable to run the full tox multi-version suite locally due to environment constraints on Windows. I ran the test suite locally with pytest across two separate environments (a global environment with SQLAlchemy 2.0.30, and this repo's own isolated uv-managed environment resolving to SQLAlchemy 2.0.51) with consistent results in both — the new regression test passes, and the only failures present are the same ones I independently confirmed exist on unmodified main (see above). Relying on CI's tox matrix to confirm full cross-version compatibility.

Manual End-to-End (E2E) Tests:
Not performed beyond the automated regression test above, which reproduces the exact failure mode (a raw float reaching the timestamp column) and confirms the read path now succeeds.

Note: the exact real-world trigger for how a raw float ends up in that column (vs. SQLAlchemy's normal text serialization) is still being confirmed with the original reporter on the issue thread. This fix correctly handles the float case whenever it occurs, regardless of how it's produced.

Checklist

  • I have read the CONTRIBUTING.md document.
  • I have performed a self-review of my own code.
  • I have commented my code, particularly in hard-to-understand areas.
  • I have added tests that prove my fix is effective or that my feature works.
  • New and existing unit tests pass locally with my changes.
  • I have manually tested my changes end-to-end.
  • Any dependent changes have been merged and published in downstream modules.

Additional context

N/A

…e float timestamps

Fixes google#6352. SQLite stores DateTime columns as REAL (Unix epoch float).
Without a result-value converter, reading them back raised
TypeError: fromisoformat: argument must be str. This adds the missing
converter, matching the naive local-time convention already used on
the write side in StorageEvent.from_event.
…lue on PreciseTimestamp

TypeDecorator.process_result_value runs after the underlying impl's own
result processor, which already fails when it receives a raw float
before process_result_value can intercept it. Overriding result_processor
directly runs before the impl's processor, allowing floats to be handled
explicitly while delegating to the impl's own processor otherwise.

Also adds a regression test that forces a raw REAL-affinity float into
the events table via raw SQL to reproduce the original failure, since
values written through the normal ORM path are already serialized as
text and do not trigger the bug.

Thanks to @surajksharma07 for catching the process_result_value issue
in review on google#6352.
@rohityan rohityan self-assigned this Jul 10, 2026
@adk-bot adk-bot added the services [Component] This issue is related to runtime services, e.g. sessions, memory, artifacts, etc label Jul 10, 2026
@rohityan

Copy link
Copy Markdown
Collaborator

Hi @abhiramArise , Thank you for your contribution! We appreciate you taking the time to submit this pull request. Can you please fix the failing CI mypy-check tests before we can proceed with the review.

@rohityan rohityan added the request clarification [Status] The maintainer need clarification or more information from the author label Jul 10, 2026
Adds type hints to PreciseTimestamp.result_processor and its nested
process() function, and uses impl_instance instead of impl to satisfy
mypy's expectation of an instance rather than a class reference when
calling result_processor on the underlying type.

Verified this introduces zero new mypy errors: the 11 remaining errors
on this file are identical (same messages) on unmodified main, all in
unrelated pre-existing methods this change doesn't touch.
@abhiramArise

Copy link
Copy Markdown
Author

@rohityan Fixed — added type annotations to result_processor (and its nested
process() function), and switched to self.impl_instance instead of self.impl
to satisfy mypy's expectation of an instance rather than a class reference.
Verified this introduces zero new mypy errors by comparing against main:
the file's 11 remaining errors are pre-existing and unrelated to this change.

Looks like the new CI run needs maintainer approval to start (Action required)

  • could you approve the workflows when you get a chance?

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

Labels

request clarification [Status] The maintainer need clarification or more information from the author services [Component] This issue is related to runtime services, e.g. sessions, memory, artifacts, etc

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DatabaseSessionService.get_session() raises TypeError: fromisoformat: argument must be str

3 participants