Repository navigation
fix: surface backend error messages in Java and Go Interactions SDK errors - #189
Open
copybara-service[bot] wants to merge 1 commit into
Open
copybara-service[bot] wants to merge 1 commit into
copybara-service[bot] wants to merge 1 commit into
Conversation
copybara-service
Bot
force-pushed
the
copybara/1015698193
branch
from
November 6, 2026 22:18
80dcaea to
ce020bc
Compare
copybara-service
Bot
force-pushed
the
copybara/1015698193
branch
2 times, most recently
from
November 11, 2026 22:47
1570a0f to
915d021
Compare
copybara-service
Bot
force-pushed
the
copybara/1015698193
branch
2 times, most recently
from
November 11, 2026 23:35
ced4416 to
2444ba2
Compare
…rrors
Since `openapi-generation` v2.960.7, Speakeasy fills the error message from `error.message` only for operations with a typed error schema (the Interactions `*ClientError` / `*ServerError` carriers in Java and Go). The untyped fallback (`GaosApiException` in Java, `APIError` in Go), used by operations without a typed error schema and by bodies that do not match it (e.g. the `[{error: {...}}]` arrays streaming endpoints return), still passes the placeholder `"API error occurred"` and leaves the backend's `{error: {message}}` only in the raw response body. So a 400 validation error from such an operation surfaces in Java as `"400 . API error occurred"` and in Go as `APIError.Message == "API error occurred"`, and no Java gaos error populates `ApiException.status()` or carries `error.details`, so even typed errors read `"400 . <message>"`.
- **Java**: Emits package-private `GaosErrorBody` (`scripts/templates/error_body.java.tmpl`). `GaosBaseException`, `GaosClientException`, and `GaosServerException` forward `GaosErrorBody.status(body)` (OnePlatform `error.status`, else the Interactions API string `error.code`, else `""`) and `GaosErrorBody.message(message, body)` to `com.google.genai.errors.ApiException`. The message is `error.message` plus any `error.details`, appended one per line after `\nDetails: `; for `{error: {...}}` bodies this matches the native `ApiException.getErrorMessageFromBody`, so gaos and native errors read the same.
- **Go**: Emits `messageFromBody` (`scripts/templates/error_body.go.tmpl`) in `interactions/models/apierrors/errorbody.go` and calls it from `NewAPIError`, which fills `APIError.Message` from `error.message`.
- Both read the `{error: {...}}` object and the single-element `[{error: {...}}]` array that streaming endpoints return, replace only the placeholder (a more specific message is kept), and keep the placeholder when the body is not a recognized JSON error (including multi-element arrays).
- **Behavior change (Java)**: `getMessage()` now spans several lines for errors that carry `error.details` (e.g. `400 INVALID_ARGUMENT. Bad thing\nDetails: {"@type":"type.googleapis.com/google.rpc.ErrorInfo","reason":"API_KEY_INVALID"}`), so callers that log or compare the text see the extra line. `message()` gains the same lines: on the per-operation `*ClientError` / `*ServerError` carriers it is the message text alone (`Bad thing\nDetails: …`), as on the native `ApiException`, while `GaosApiException` keeps Speakeasy's existing override that returns the full `getMessage()` string. Go's `APIError.Message` stays `error.message` only; details remain in the body that `Error()` appends, following each language's native convention.
- **Documented differences from the native Java `ApiException`**: `status()` falls back to the Interactions `error.code` where native uses the HTTP reason phrase (empty on HTTP/2); a single-element array also yields its details and an empty missing status where native `throwFromErrorNode` drops details and reports `UNKNOWN`; a multi-element array keeps the placeholder where native reads the first element. These are stated in the `GaosErrorBody` Javadoc and pinned by tests.
- **Sync hardening & tests**: `reparent_java_error` replacements in `sync_speakeasy_outputs.py` now use `_replace_once`, so generator template changes fail loudly at sync time; adds `patch_go_apierrors`; adds Java (including parity and divergence tests against the native `ApiException`), Go, and sync unit tests.
PiperOrigin-RevId: 1015698193
copybara-service
Bot
force-pushed
the
copybara/1015698193
branch
from
November 11, 2026 23:54
2444ba2 to
462f2be
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
fix: surface backend error messages in Java and Go Interactions SDK errors
Since
openapi-generationv2.960.7, Speakeasy fills the error message fromerror.messageonly for operations with a typed error schema (the Interactions*ClientError/*ServerErrorcarriers in Java and Go). The untyped fallback (GaosApiExceptionin Java,APIErrorin Go), used by operations without a typed error schema and by bodies that do not match it (e.g. the[{error: {...}}]arrays streaming endpoints return), still passes the placeholder"API error occurred"and leaves the backend's{error: {message}}only in the raw response body. So a 400 validation error from such an operation surfaces in Java as"400 . API error occurred"and in Go asAPIError.Message == "API error occurred", and no Java gaos error populatesApiException.status()or carrieserror.details, so even typed errors read"400 . <message>".GaosErrorBody(scripts/templates/error_body.java.tmpl).GaosBaseException,GaosClientException, andGaosServerExceptionforwardGaosErrorBody.status(body)(OnePlatformerror.status, else the Interactions API stringerror.code, else"") andGaosErrorBody.message(message, body)tocom.google.genai.errors.ApiException. The message iserror.messageplus anyerror.details, appended one per line after\nDetails:; for{error: {...}}bodies this matches the nativeApiException.getErrorMessageFromBody, so gaos and native errors read the same.messageFromBody(scripts/templates/error_body.go.tmpl) ininteractions/models/apierrors/errorbody.goand calls it fromNewAPIError, which fillsAPIError.Messagefromerror.message.{error: {...}}object and the single-element[{error: {...}}]array that streaming endpoints return, replace only the placeholder (a more specific message is kept), and keep the placeholder when the body is not a recognized JSON error (including multi-element arrays).getMessage()now spans several lines for errors that carryerror.details(e.g.400 INVALID_ARGUMENT. Bad thing\nDetails: {"@type":"type.googleapis.com/google.rpc.ErrorInfo","reason":"API_KEY_INVALID"}), so callers that log or compare the text see the extra line.message()gains the same lines: on the per-operation*ClientError/*ServerErrorcarriers it is the message text alone (Bad thing\nDetails: …), as on the nativeApiException, whileGaosApiExceptionkeeps Speakeasy's existing override that returns the fullgetMessage()string. Go'sAPIError.Messagestayserror.messageonly; details remain in the body thatError()appends, following each language's native convention.ApiException:status()falls back to the Interactionserror.codewhere native uses the HTTP reason phrase (empty on HTTP/2); a single-element array also yields its details and an empty missing status where nativethrowFromErrorNodedrops details and reportsUNKNOWN; a multi-element array keeps the placeholder where native reads the first element. These are stated in theGaosErrorBodyJavadoc and pinned by tests.reparent_java_errorreplacements insync_speakeasy_outputs.pynow use_replace_once, so generator template changes fail loudly at sync time; addspatch_go_apierrors; adds Java (including parity and divergence tests against the nativeApiException), Go, and sync unit tests.