Skip to content

fix(session/database): maintain UpdateTime on app/user state writes - #1204

Merged
wolo-lab merged 2 commits into
google:mainfrom
PratikDhanaveFork:fix/state-update-time
Aug 27, 2026
Merged

wolo-lab merged 2 commits into
google:mainfrom
PratikDhanaveFork:fix/state-update-time

Conversation

@PratikDhanave

Copy link
Copy Markdown
Contributor

Summary

Fixes #1177.

storageAppState / storageUserState (session/database) were saved without ever assigning UpdateTime, so Go's zero time.Time was written explicitly. MySQL rejects that under its default strict mode (NO_ZERO_DATE / STRICT_TRANS_TABLES):

failed to save app state: Error 1292 (22007): Incorrect datetime value:
'0000-00-00' for column 'update_time' at row 1

SQLite, PostgreSQL and Spanner accept the zero value, so only MySQL failed — whenever app: / user: scoped state was persisted (on Create, or via an event's state delta in AppendEvent). A DDL-level DEFAULT CURRENT_TIMESTAMP can't fix it: the column is present in the generated statement, so the explicit (invalid) zero value always wins.

Change

Assign UpdateTime before saving both state tables, mirroring how storageSession.UpdateTime is already maintained:

  • Create → createdSession.UpdateTime (so app/user/session share one create timestamp)
  • AppendEvent/applyEvent → event.Timestamp (matching the adjacent storageSess.UpdateTime = event.Timestamp)

I set it explicitly rather than adding a GORM autoUpdateTime tag on purpose: autoUpdateTime calls time.Now() internally and would bypass the repo's platform.Now(ctx) seam that makes time deterministic in tests (e.g. Test_databaseService_CreateUsesProviders).

Testing Plan

  • Added TestDatabaseService_StateUpdateTimeIsSet: creates a session with app:/user: scoped state and appends an event carrying app:/user: deltas, then reads the app_states / user_states rows back and asserts UpdateTime is populated (non-zero) on both the Create and AppendEvent paths.
    • The bug only errors on MySQL, so rather than require MySQL the test asserts the precondition the failure depends on — a zero update_time — is gone. (White-box: the test is in package database and queries the storage tables via the unexported handle.)
  • Verified it's a genuine regression guard: passes with the fix, fails on all four assertions against the pre-fix code.
  • go test -race -count=1 ./session/database/ — pass (full package)
  • go build -mod=readonly ./... — pass
  • go vet ./session/database/ — pass
  • golangci-lint run ./session/database/ — 0 issues
  • gofmt -l — clean

storageAppState / storageUserState were saved without ever assigning
UpdateTime, so Go's zero time.Time was written explicitly. MySQL rejects
that under its default strict mode (NO_ZERO_DATE / STRICT_TRANS_TABLES):

  failed to save app state: Error 1292 (22007): Incorrect datetime value:
  '0000-00-00' for column 'update_time' at row 1

SQLite, PostgreSQL and Spanner accept the zero value, so only MySQL failed
whenever app:/user: scoped state was persisted (on Create or via an event's
state delta). A DDL default can't fix it: the column is present in the
generated statement, so the explicit zero value always wins.

Assign UpdateTime before saving both state tables, mirroring how
storageSession.UpdateTime is already maintained: createdSession.UpdateTime
on Create and event.Timestamp on AppendEvent. Setting it explicitly (rather
than a GORM autoUpdateTime tag) keeps the platform.Now time provider seam
intact for deterministic tests.

Fixes google#1177
@wolo-lab
wolo-lab merged commit 245ba28 into google:main Aug 27, 2026
14 checks passed
wolo-lab added a commit that referenced this pull request Sep 4, 2026
…d in this backport

The #1204 cherry-pick added TestDatabaseService_StateUpdateTimeIsSet, and the
#1237 cherry-pick two commits later dropped it while re-anchoring the test file
around the workflow-event test that does not exist on this line. That left the
#1204 fix shipping with no coverage: with all four UpdateTime assignments
removed from service.go, the whole session tree stayed green.

Restored verbatim from main, so the two branches carry the same test.
jjsasha63 pushed a commit that referenced this pull request Sep 4, 2026
* fix(session/vertexai): treat nil function args as an empty struct (#1322)

toStructPB marshals its input to JSON and hands the result to
structpb.Struct.UnmarshalJSON. A nil map marshals to the literal null,
which structpb rejects, so toStructPB returns an error where the
structpb.NewStruct call it replaced returned an empty Struct.

createAiplatformpbContent calls toStructPB unconditionally for
FunctionCall.Args and FunctionResponse.Response, unlike the other call
sites which are guarded by a length check. A tool that declares no
parameters produces a FunctionCall with nil Args, so the conversion
fails, AppendEvent returns an error and the event is never persisted.
The same applies to a tool that returns no payload.

Treat a null marshaling as an empty Struct, restoring the previous
behaviour for every caller.

Fixes #1321

(cherry picked from commit c88d496)

* fix(session/database): maintain UpdateTime on app/user state writes (#1204)

storageAppState / storageUserState were saved without ever assigning
UpdateTime, so Go's zero time.Time was written explicitly. MySQL rejects
that under its default strict mode (NO_ZERO_DATE / STRICT_TRANS_TABLES):

  failed to save app state: Error 1292 (22007): Incorrect datetime value:
  '0000-00-00' for column 'update_time' at row 1

SQLite, PostgreSQL and Spanner accept the zero value, so only MySQL failed
whenever app:/user: scoped state was persisted (on Create or via an event's
state delta). A DDL default can't fix it: the column is present in the
generated statement, so the explicit zero value always wins.

Assign UpdateTime before saving both state tables, mirroring how
storageSession.UpdateTime is already maintained: createdSession.UpdateTime
on Create and event.Timestamp on AppendEvent. Setting it explicitly (rather
than a GORM autoUpdateTime tag) keeps the platform.Now time provider seam
intact for deterministic tests.

Fixes #1177

Co-authored-by: wolo <[email protected]>

(cherry picked from commit 245ba28)

* fix(session/database): format stale-session timestamps as UnixMicro (#1237)

The stale-session error compared UnixMicro() values (microseconds since
epoch) but formatted them with time.Unix(0, x), whose second argument is
nanoseconds. Being off by 1000x, every real timestamp collapsed to
1970-01-2x, so operators debugging optimistic-concurrency rejections saw:

  stale session error: last update time from request
  (1970-01-21T15:38:42.372056389Z) is older than in database
  (1970-01-21T15:38:42.372500103Z)

instead of the actual wall-clock times. The comparison itself is unaffected
(both sides share the same unit); only the display strings were wrong.

Use time.UnixMicro, which interprets the value as microseconds. Adds a
regression test that triggers the stale path via an optimistic-concurrency
conflict and asserts the rendered timestamps are the real times, not 1970.

Fixes #1170

Co-authored-by: wolo <[email protected]>

(cherry picked from commit 3f69c50)

* fix(session/vertexai): quote userId in AIP-160 list filter (#1171)

listSessions built the Vertex AI Agent Engine list filter by interpolating the
raw UserID into a quoted AIP-160 string literal
(fmt.Sprintf("userId=\"%s\"", req.UserID)). A double quote in UserID breaks out
of the literal and appends attacker-controlled predicates, e.g. UserID
`attacker" OR userId!="` yields the filter `userId="attacker" OR userId!=""`,
which returns sessions for every user of the deployment (cross-user session
enumeration).

Add quoteFilterLiteral, which escapes backslashes then double quotes so the
value stays inside the literal, and use it when building the filter. Adds a
regression test covering quote injection, backslash-only, and empty input.

This is the Go counterpart of the same issue previously fixed in adk-python
(google/adk-python#5270, PR google/adk-python#5273, commit bdece00).

Co-authored-by: wolo <[email protected]>

(cherry picked from commit 78b8355)

* test(session/database): restore the UpdateTime regression test dropped in this backport

The #1204 cherry-pick added TestDatabaseService_StateUpdateTimeIsSet, and the
#1237 cherry-pick two commits later dropped it while re-anchoring the test file
around the workflow-event test that does not exist on this line. That left the
#1204 fix shipping with no coverage: with all four UpdateTime assignments
removed from service.go, the whole session tree stayed green.

Restored verbatim from main, so the two branches carry the same test.
kdroste-google pushed a commit to kdroste-google/adk-go that referenced this pull request Sep 29, 2026
…oogle#1204)

storageAppState / storageUserState were saved without ever assigning
UpdateTime, so Go's zero time.Time was written explicitly. MySQL rejects
that under its default strict mode (NO_ZERO_DATE / STRICT_TRANS_TABLES):

  failed to save app state: Error 1292 (22007): Incorrect datetime value:
  '0000-00-00' for column 'update_time' at row 1

SQLite, PostgreSQL and Spanner accept the zero value, so only MySQL failed
whenever app:/user: scoped state was persisted (on Create or via an event's
state delta). A DDL default can't fix it: the column is present in the
generated statement, so the explicit zero value always wins.

Assign UpdateTime before saving both state tables, mirroring how
storageSession.UpdateTime is already maintained: createdSession.UpdateTime
on Create and event.Timestamp on AppendEvent. Setting it explicitly (rather
than a GORM autoUpdateTime tag) keeps the platform.Now time provider seam
intact for deterministic tests.

Fixes google#1177

Co-authored-by: wolo <[email protected]>
li-xuankun312 added a commit to li-xuankun312/einsteinOS that referenced this pull request Sep 29, 2026
…oogle#1204)

storageAppState / storageUserState were saved without ever assigning
UpdateTime, so Go's zero time.Time was written explicitly. MySQL rejects
that under its default strict mode (NO_ZERO_DATE / STRICT_TRANS_TABLES):

  failed to save app state: Error 1292 (22007): Incorrect datetime value:
  '0000-00-00' for column 'update_time' at row 1

SQLite, PostgreSQL and Spanner accept the zero value, so only MySQL failed
whenever app:/user: scoped state was persisted (on Create or via an event's
state delta). A DDL default can't fix it: the column is present in the
generated statement, so the explicit zero value always wins.

Assign UpdateTime before saving both state tables, mirroring how
storageSession.UpdateTime is already maintained: createdSession.UpdateTime
on Create and event.Timestamp on AppendEvent. Setting it explicitly (rather
than a GORM autoUpdateTime tag) keeps the platform.Now time provider seam
intact for deterministic tests.

Fixes google#1177

Co-authored-by: wolo <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

session/database: persisting app/user state fails on MySQL (strict mode) — zero-value update_time

2 participants