Skip to content

fix(agentengine): base64-encode []byte fields instead of failing thei… - #1387

Open
rizukijr wants to merge 1 commit into
google:mainfrom
rizukijr:fix-thoughtsignature-encode-bug
Open

fix(agentengine): base64-encode []byte fields instead of failing thei…#1387
rizukijr wants to merge 1 commit into
google:mainfrom
rizukijr:fix-thoughtsignature-encode-bug

Conversation

@rizukijr

@rizukijr rizukijr commented Aug 22, 2026

Copy link
Copy Markdown

Link to Issue or Description of Change

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

Testing Plan

Unit Tests:

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

Added two tests to encode_test.go, following the existing file's conventions:

  • TestByteSlice - a session.Event with a non-empty
    Content.Parts[0].ThoughtSignature now converts successfully, and the
    resulting thought_signature key holds the expected base64 string
    ({0xDE, 0xAD, 0xBE, 0xEF} -> "3q2+7w==", matching what
    encoding/json produces for the same bytes).
  • TestByteSliceOmitEmpty - confirms omitempty is unaffected: an empty
    []byte is still correctly omitted, not encoded as an empty string.

Ran the full existing package suite alongside the new tests - zero regressions:

$ go test ./server/agentengine/...
ok      google.golang.org/adk/v2/server/agentengine/controllers/method  0.972s
ok      google.golang.org/adk/v2/server/agentengine/internal/helper     0.491s
$ go vet ./server/agentengine/...
(clean)

Manual End-to-End (E2E) Tests:

Ran the real agentengine launcher (web agentengine - the same launcher
composition agentengine.NewLauncher uses in production) locally against a
real deployed, Vertex AI Sessions-backed Reasoning Engine resource, using
gemini-3.6-flash (emits a non-empty ThoughtSignature on essentially
every turn, not just tool calls - the worst case observed). Sent the exact
async_stream_query request shape Gemini Enterprise uses:

curl -s -X POST -H "Content-Type: application/json" \
  "http://localhost:8080/api/stream_reasoning_engine" \
  -d '{"class_method": "async_stream_query", "input": {"user_id": "verify", "message": {"role": "user", "parts": [{"text": "hi"}]}}}'
  • Before the fix: every signature-bearing event logged Failed to convert: ... err: unsupported type: uint8 and shipped to the client in raw
    Go/camelCase field names (thoughtSignature, finishReason,
    invocationId) mixed into an otherwise snake_case stream.
  • After the fix: zero Failed to convert log lines. The event now
    serializes correctly, e.g. "thought_signature": "AY89a1...", with
    consistent snake_case throughout (finish_reason, invocation_id, etc).

Also confirmed this is not model- or version-specific: the same missing
reflect.Uint8 case is present identically in all three published
adk/v2 releases (v2.0.0, v2.1.0, v2.2.0), and the bug reproduces on
gemini-2.5-flash too on any function-calling turn (just not on every
turn, unlike gemini-3.6-flash). Full details, including the live Gemini
Enterprise retry-into-dead-session cascade this caused in production, are
in #1386.

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.

Additional context

convertSnake's type switch only ever handled signed integers
(Int/Int8/Int16/Int32/Int64) - there was no case for Uint8 or
any other Uint* kind. A []byte field is a reflect.Slice, so it fell
into the generic per-element loop, and each individual byte (a bare
uint8) hit the default: unsupported type branch, failing conversion of
the entire containing struct. encoding/json.Marshal already solves
this correctly (base64-encodes []byte automatically) - this fix just
applies the same, standard approach instead of reinventing a worse version
of it for one specific hand-rolled converter.

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.

[]byte fields (e.g. genai.Part.ThoughtSignature) fail helper.ConvertSnake with "unsupported type: uint8", breaking Agent Engine event JSON

1 participant