Skip to content

feat(js): honor Retry-After header & never retry AbortError/ToolInterruptError - #5343

Merged
pavelgj merged 6 commits into
mainfrom
pj/retry-after
May 21, 2026
Merged

pavelgj merged 6 commits into
mainfrom
pj/retry-after

Conversation

@pavelgj

@pavelgj pavelgj commented May 19, 2026 •

Copy link
Copy Markdown
Member

Fixes #5270
Fixes #5331

What

Make the retry middleware to respect the Retry-After HTTP header from providers, and prevents retrying errors that should never be retried.

Changes

Core (@genkit-ai/core)

  • Added ErrorResponseMetadata interface with retryAfterMs and headers fields
  • Added optional responseMetadata property to GenkitError (not serialized in toJSON() — in-process only)

Retry middleware (@genkit-ai/middleware)

  • Uses responseMetadata.retryAfterMs as a floor for the retry delay (Math.max(computedDelay, retryAfterMs))
  • Never retries AbortError (from AbortController) or ToolInterruptError (control-flow signal), regardless of error type
  • Added 4 new unit tests covering both behaviors

Provider plugins — all now capture Retry-After from HTTP responses and pass it as responseMetadata on GenkitError:

  • google-genai: googleai/client.ts and vertexai/client.ts read from Response.headers
  • compat-oai: reads from OpenAI SDK APIError.headers
  • anthropic: added try/catch around runner.run() to convert Anthropic SDK APIError → GenkitError with status mapping (429→RESOURCE_EXHAUSTED, 401→UNAUTHENTICATED, etc.) and Retry-After propagation

Notes

  • parseRetryAfterMs supports both delay-seconds and HTTP-date formats (RFC 7231 §7.1.3), with guards against empty/whitespace input
  • retryAfterMs uses !== undefined checks to preserve Retry-After: 0 as a valid "retry immediately" signal
  • The anthropic plugin previously let raw SDK errors propagate; it now normalizes them to GenkitError with proper status codes

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.
@github-actions github-actions Bot added the js label May 19, 2026
@pavelgj pavelgj changed the title feat: add ErrorResponseMetadata to GenkitError for retry-after support feat(js): add ErrorResponseMetadata to GenkitError for retry-after support May 19, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread js/plugins/google-genai/src/common/utils.ts
Comment thread js/plugins/google-genai/src/googleai/client.ts Outdated
Comment thread js/plugins/google-genai/src/vertexai/client.ts Outdated
Comment thread js/plugins/middleware/src/retry.ts
Comment thread js/plugins/middleware/src/retry.ts Outdated
pavelgj added 2 commits May 19, 2026 10:34
…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
@pavelgj

pavelgj commented May 19, 2026

Copy link
Copy Markdown
Member Author

/gemini review

@pavelgj pavelgj changed the title feat(js): add ErrorResponseMetadata to GenkitError for retry-after support feat(js): honor Retry-After header & never retry AbortError/ToolInterruptError May 19, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@pavelgj
pavelgj requested a review from MichaelDoyle May 19, 2026 15:10
@pavelgj
pavelgj marked this pull request as ready for review May 19, 2026 15:10
Comment thread js/plugins/anthropic/src/models.ts
Comment thread js/plugins/compat-oai/src/model.ts
Comment thread js/plugins/google-genai/src/common/utils.ts Outdated
Move parseRetryAfterMs from TEST_ONLY exports to a direct public import
and add comprehensive unit tests covering
Comment thread js/plugins/anthropic/src/models.ts
Co-authored-by: Michael Doyle <michaeldoyle@google.com>
@pavelgj
pavelgj merged commit c9b003a into main May 21, 2026
8 checks passed
@pavelgj
pavelgj deleted the pj/retry-after branch May 21, 2026 01:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

4 participants