Skip to content

Conversation

@filipecaixeta
Copy link

@filipecaixeta filipecaixeta commented Feb 3, 2026

Summary

  • Fixes DataError when using PostgreSQL with asyncpg for session storage
  • PostgreSQL's default TIMESTAMP type is WITHOUT TIME ZONE, which cannot accept timezone-aware datetime objects
  • The existing code handled this for SQLite but not PostgreSQL - this fix applies the same timezone stripping

Error

When creating a session with PostgreSQL + asyncpg, the following error occurs:

sqlalchemy.dialects.postgresql.asyncpg.Error: <class 'asyncpg.exceptions.DataError'>: 
invalid input for query argument $5: datetime.datetime(2026, 2, 3, 21, 32, 50, 353909, 
tzinfo=datetime.timezone.utc) (can't subtract offset-naive and offset-aware datetimes)

During the INSERT:

INSERT INTO sessions (app_name, user_id, id, state, create_time, update_time) 
VALUES ($1, $2, $3, $4, $5, $6)

Where $5 and $6 are timezone-aware datetimes being inserted into TIMESTAMP WITHOUT TIME ZONE columns.

Root Cause

Commit 1063fa5 changed from database-generated timestamps (func.now()) to explicit Python datetimes (datetime.now(timezone.utc)). The SQLite case was handled by stripping the timezone, but PostgreSQL was overlooked.

Test plan

  • Verified fix resolves the error when creating sessions with PostgreSQL + asyncpg
  • Existing unit tests pass

Fixes regression from #1733

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @filipecaixeta, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request resolves a critical DataError in PostgreSQL by implementing consistent timezone stripping for timestamps, mirroring the existing behavior for SQLite to accommodate PostgreSQL's TIMESTAMP WITHOUT TIME ZONE type. Concurrently, it enhances the A2A to GenAI part conversion mechanism by introducing and supporting a new thought metadata field across various part types, enabling richer contextual data transfer.

Highlights

  • PostgreSQL Timestamp Fix: Addressed a 'DataError: can't subtract offset-naive and offset-aware datetimes' that occurred when using PostgreSQL with asyncpg. This error stemmed from attempting to store timezone-aware datetime objects into PostgreSQL's default TIMESTAMP WITHOUT TIME ZONE column type.
  • Consistent Timezone Stripping: Extended the existing timezone stripping logic, previously applied only to SQLite, to now also include PostgreSQL. This ensures that datetime.now(timezone.utc) objects are converted to timezone-naive before being stored, aligning with the database's expected format.
  • New 'thought' Metadata Support: Introduced a new thought metadata field for various Part types (TextPart, FilePart, FunctionCall, FunctionResponse, CodeExecutionResult, ExecutableCode) during the conversion process between A2A and GenAI formats. This allows for the propagation of additional contextual information.
  • Expanded Unit Tests for 'thought' Metadata: Added comprehensive unit tests to validate the correct handling and round-trip conversion of the new thought metadata across all supported Part types, ensuring data integrity during format transformations.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • src/google/adk/a2a/converters/part_converter.py
    • Modified convert_a2a_part_to_genai_part to extract and assign a thought field from part.metadata to the genai_types.Part for TextPart, FilePart (URI and Bytes), FunctionCall, FunctionResponse, CodeExecutionResult, and ExecutableCode.
    • Modified convert_genai_part_to_a2a_part to include the thought field in the a2a_part.metadata when converting from GenAI to A2A for FilePart, DataPart (FunctionCall, FunctionResponse, CodeExecutionResult, ExecutableCode), and Blob types.
  • src/google/adk/sessions/database_session_service.py
    • Updated the create_session function to strip timezone information from datetime.now(timezone.utc) when the database dialect is 'postgresql', in addition to 'sqlite'.
  • tests/unittests/a2a/converters/test_part_converter.py
    • Added new unit tests to verify the conversion of A2A TextPart, FilePart (with URI and Bytes), FunctionCall, FunctionResponse, CodeExecutionResult, and ExecutableCode to GenAI Part, specifically checking for the correct handling of the thought metadata field (true, false, or none).
    • Added new round-trip tests to ensure that the thought metadata is correctly preserved when converting from GenAI Part to A2A Part and back to GenAI Part for TextPart, FilePart (with URI and Bytes), FunctionCall, FunctionResponse, CodeExecutionResult, and ExecutableCode.
    • Modified the UnsupportedPartType mock class to include a metadata = None attribute to prevent errors in updated conversion logic.
Activity
  • The author verified that the fix resolves the DataError when creating sessions with PostgreSQL and asyncpg.
  • Existing unit tests were confirmed to pass after the changes.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@adk-bot adk-bot added the services [Component] This issue is related to runtime services, e.g. sessions, memory, artifacts, etc label Feb 3, 2026
@adk-bot
Copy link
Collaborator

adk-bot commented Feb 3, 2026

Response from ADK Triaging Agent

Hello @filipecaixeta, thank you for creating this PR!

This PR is a bug fix. Could you please associate the GitHub issue with this PR? If there is no existing issue, could you please create one?

In addition, could you please provide logs or a screenshot after the fix is applied?

This information will help reviewers to review your PR more efficiently. Thanks!

PostgreSQL's default TIMESTAMP type is WITHOUT TIME ZONE, which cannot
accept timezone-aware datetime objects from Python. This causes a
DataError when using asyncpg: "can't subtract offset-naive and
offset-aware datetimes".

The existing code already handled this for SQLite by stripping the
timezone, but PostgreSQL was not handled. This fix applies the same
treatment to PostgreSQL.

Fixes the regression introduced in commit 1063fa5 which changed from
database-generated timestamps (func.now()) to explicit Python datetimes.
@filipecaixeta filipecaixeta force-pushed the fix-postgresql-timestamp-timezone branch from 0022eb3 to e275b01 Compare February 3, 2026 22:22
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request primarily addresses a timezone-related issue with PostgreSQL timestamps, which is a necessary fix. The change in DatabaseSessionService is correct and I've added a minor suggestion for simplification. Additionally, a significant portion of this PR introduces support for thought metadata in part conversions. This seems to be a new feature and is well-tested. I've included a suggestion to refactor some duplicated code in the converter to improve maintainability. It would be beneficial to update the PR description to reflect the addition of the thought feature for better clarity and tracking.

I am having trouble creating individual review comments. Click here to see my feedback.

src/google/adk/a2a/converters/part_converter.py (229-295)

medium

There's significant code duplication in handling the conversion for function_call, function_response, code_execution_result, and executable_code. All these blocks follow the same pattern of creating a metadata dictionary and then a DataPart. This can be refactored to reduce repetition and improve maintainability by determining the part_data and metadata_type first, and then constructing the DataPart in a single common block.

  part_data = None
  metadata_type = None

  if part.function_call:
    part_data = part.function_call
    metadata_type = A2A_DATA_PART_METADATA_TYPE_FUNCTION_CALL
  elif part.function_response:
    part_data = part.function_response
    metadata_type = A2A_DATA_PART_METADATA_TYPE_FUNCTION_RESPONSE
  elif part.code_execution_result:
    part_data = part.code_execution_result
    metadata_type = A2A_DATA_PART_METADATA_TYPE_CODE_EXECUTION_RESULT
  elif part.executable_code:
    part_data = part.executable_code
    metadata_type = A2A_DATA_PART_METADATA_TYPE_EXECUTABLE_CODE

  if part_data:
    metadata = {
        _get_adk_metadata_key(A2A_DATA_PART_METADATA_TYPE_KEY): metadata_type
    }
    if part.thought is not None:
      metadata[_get_adk_metadata_key('thought')] = part.thought
    return a2a_types.Part(
        root=a2a_types.DataPart(
            data=part_data.model_dump(by_alias=True, exclude_none=True),
            metadata=metadata,
        )
    )

src/google/adk/sessions/database_session_service.py (299-302)

medium

The check for database dialects can be simplified to be more concise and maintainable. You can combine the checks into a single if statement using the in operator with a tuple of dialect names. This removes the need for intermediate variables and is easier to extend in the future.

      if self.db_engine.dialect.name in ("sqlite", "postgresql"):
        now = now.replace(tzinfo=None)

…ervice

Adds unit tests to verify that timezone-aware datetimes are correctly
converted to naive datetimes for SQLite and PostgreSQL dialects, which
require naive timestamps for their default TIMESTAMP column types.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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.

2 participants