Repository navigation
feat(js): honor Retry-After header & never retry AbortError/ToolInterruptError - #5343
Conversation
Introduces `ErrorResponseMetadata` interface on `GenkitError` to carry HTTP response metadata (e.g. `Retry-After` header) in-process without serializing it to the wire format. Adds `parseRetryAfterMs` utility supporting both delay-seconds and HTTP-date formats, and wires it into the Google GenAI plugin's error handling so retry middleware can respect provider-suggested backoff delays.
There was a problem hiding this comment.
Code Review
This pull request introduces ErrorResponseMetadata to GenkitError, enabling the capture of HTTP response details such as Retry-After headers. This allows the retry middleware to honor provider-suggested delays and includes a new blacklist for errors that should never be retried, specifically AbortError and ToolInterruptError. Feedback focuses on improving the robustness of the Retry-After parsing logic to handle whitespace and zero-value delays, ensuring the error blacklist is applied consistently across all error types, and adding null-safety checks when accessing error properties.
…dleware Add `ErrorResponseMetadata` type with `retryAfterMs` field to `GenkitError`, enabling model plugins to surface `Retry-After` header values from upstream API errors. The retry middleware now respects these values to delay retries appropriately. - Add `parseRetryAfterMs` helper (supports seconds and HTTP-date formats) to Anthropic and compat-oai plugins - Map Anthropic `APIError` to `GenkitError` with proper status codes - Attach `retryAfterMs` response metadata in both Anthropic and OpenAI compatible error handlers - Update retry middleware to wait for `retryAfterMs` when present - Export `ErrorResponseMetadata` and `StatusName` types from genkit core
The value `retryAfterMs: 0` (meaning "retry immediately") was being treated as falsy and discarded. This fixes: - `parseRetryAfterMs` to guard against empty/whitespace input - Truthy checks changed to explicit `!== undefined` so 0 is preserved - Retry middleware refactored to respect `retryAfterMs` from error metadata, including the zero-delay case - Added corresponding unit tests across affected plugins
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces a mechanism to honor the Retry-After HTTP header from various model providers (Anthropic, OpenAI, and Google GenAI) by capturing it in a new responseMetadata field within GenkitError. The retry middleware has been updated to use this metadata as a minimum delay floor and to explicitly avoid retrying specific errors such as AbortError and ToolInterruptError. I have no feedback to provide.
Move parseRetryAfterMs from TEST_ONLY exports to a direct public import and add comprehensive unit tests covering
Co-authored-by: Michael Doyle <michaeldoyle@google.com>
Fixes #5270
Fixes #5331
What
Make the retry middleware to respect the
Retry-AfterHTTP header from providers, and prevents retrying errors that should never be retried.Changes
Core (
@genkit-ai/core)ErrorResponseMetadatainterface withretryAfterMsandheadersfieldsresponseMetadataproperty toGenkitError(not serialized intoJSON()— in-process only)Retry middleware (
@genkit-ai/middleware)responseMetadata.retryAfterMsas a floor for the retry delay (Math.max(computedDelay, retryAfterMs))AbortError(fromAbortController) orToolInterruptError(control-flow signal), regardless of error typeProvider plugins — all now capture
Retry-Afterfrom HTTP responses and pass it asresponseMetadataonGenkitError:googleai/client.tsandvertexai/client.tsread fromResponse.headersAPIError.headersrunner.run()to convert Anthropic SDKAPIError→GenkitErrorwith status mapping (429→RESOURCE_EXHAUSTED, 401→UNAUTHENTICATED, etc.) andRetry-AfterpropagationNotes
parseRetryAfterMssupports both delay-seconds and HTTP-date formats (RFC 7231 §7.1.3), with guards against empty/whitespace inputretryAfterMsuses!== undefinedchecks to preserveRetry-After: 0as a valid "retry immediately" signalGenkitErrorwith proper status codes