Skip to content

fix: handle a reply cut off part-way through an A2UI message - #29

Merged
brianegan merged 4 commits into
mainfrom
25-fix-example-error
Sep 24, 2026
Merged

brianegan merged 4 commits into
mainfrom
25-fix-example-error

Conversation

@brianegan

Copy link
Copy Markdown
Collaborator

Summary

When Gemini's recitation filter stopped a reply, the example chat showed half-written A2UI JSON as prose, then an opaque "Internal server error". The recitation stop sends a chunk with empty content, and genkit_google_genai crashes on it with a null check that Genkit reports as INTERNAL. The plugin bug belongs upstream. This PR makes the package and the example handle a reply that is cut off.

Changes

  • A2uiParserTransformer reports an A2UI message that is still unfinished when the stream ends as an A2uiValidationException, so it arrives as a GenUiError. This applies whether the stream ended cleanly or failed. The leftover counts only when it opens with a ```json fence, or with a brace or fence that names a message key. A stray brace or an unrelated code block still shows as prose.
  • The example shows "The model stopped before finishing. Try again, or ask a different way." for a failed turn and logs the raw failure to the browser console. Whatever the reply managed to produce stays in the transcript.
  • The example's chatHandler logs a failed turn to stderr with its cause and stack trace. A model failure doesn't throw out of a Genkit agent. It ends the turn with AgentOutput.error, so the wrapper reads the error there, before genkit_shelf hides it behind a 500.
  • The example prompt asks the model to write in its own words and not quote reference text, which makes recitation stops less likely.
  • The README lists a message cut off when the reply ended as one kind of GenUiError.

Testing

The best check is the real failure. Run the example with GEMINI_API_KEY set, and send a prompt that asks for stock text word for word, for example: "Quote verbatim the Google knowledge panel description of Paris that begins: Paris, France's capital, is a major European city and a global center for art, fashion, gastronomy and culture. Continue it word for word." Recitation doesn't trigger on every run, so it can take a few tries.

When it does trigger, the model's opening sentence stays in the bubble with no JSON after it, a red line says the reply ended part-way through an A2UI message, and the plain failure message appears under the transcript. The server terminal should show Chat turn failed: INTERNAL: Google AI Error: Null check operator used on a null value with a stack trace that points at fromGeminiCandidate in genkit_google_genai.

Without a key, test/transport/parser_test.dart covers the truncation cases, example/test/browser/chat_browser_test.dart covers the failed-turn UI, and example/test/round_trip_test.dart covers the server log.

Gates

Gated 496db48 against origin/main@5350ed7.

  • review: skill code-review on fable, passed, agent-reported
  • test: command, passed in 84.2s
  • example-test: command, passed in 16.1s
  • browser-test: command, passed in 6.6s

Reviewed against the plan recorded on the issue.

Closes #25

Gemini stops a reply that trips its recitation filter with an empty
content object, which genkit_google_genai's fromGeminiCandidate crashes
on. The parser now reports an unfinished A2UI message at stream end
instead of printing it as prose, the example shows a plain message for
a failed turn and logs the cause on the server, and the example prompt
asks the model to write in its own words.

Closes #25
@brianegan
brianegan merged commit 00531be into main Sep 24, 2026
8 checks passed
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.

Fix Example error

1 participant