Menu

#106 OpenAI retry handling ignores Gemini error.details[].retryDelay metadata

open
nobody
None
2026-08-11
2026-08-11
Anonymous
No

Originally created by: yablokolabs

Summary

The OpenAI-compatible HTTP path now has bounded retries for 429/5xx responses and supports the standard Retry-After header. However, Gemini's OpenAI-compatible endpoint can return its cooldown only in structured error JSON, with no Retry-After header:

{
  "error": {
    "code": 429,
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.RetryInfo",
        "retryDelay": "39s"
      }
    ]
  }
}

Atomic currently ignores this metadata. It retries using the 150/300 ms backoff and can exhaust all attempts before the provider's advertised cooldown has elapsed.

This behavior was observed on Atomic Agent v0.1.72. Generic cloud retries were added on main in 705e601, so this issue is intentionally limited to the remaining provider-specific metadata gap rather than duplicating closed [#13].

Current behavior

httpErrorFromResponse reads the response body for an error preview but derives retryAfterMs only from the HTTP header.

resolveWaitMs therefore sees no provider cooldown and uses only the short exponential backoff.

Expected behavior

For retryable 429/503 responses, Atomic should also recognize well-known structured retry metadata when Retry-After is absent. It should not burn all attempts before the provider-declared cooldown.

The retry remains bounded: if the declared delay exceeds the interactive retry budget, Atomic can surface a typed cooldown error instead of sleeping indefinitely.

Suggested implementation

  • Prefer a valid standard Retry-After header.
  • Otherwise inspect bounded JSON error details for Google RetryInfo.retryDelay values such as 39s or 1.5s.
  • Apply the existing retry cap/policy deliberately: either wait within budget or stop without issuing premature retries when the required delay is longer than that budget.
  • Preserve caller cancellation and the existing rule that deterministic 4xx errors are not retried.

The evaluation's localhost Gemini bridge parsed error.details[].retryDelay, added a small safety margin, and enforced a maximum retry count and delay cap.

Acceptance criteria

  • A 429 response with no header but error.details[].retryDelay controls retry timing.
  • Fractional-second values are parsed correctly; malformed or negative values are ignored safely.
  • Retry-After header behavior remains unchanged and takes precedence when present.
  • A provider delay larger than the allowed budget does not cause three immediate retries or an unbounded sleep.
  • 503 responses can use the same metadata path.
  • Caller cancellation interrupts any pending wait.
  • Regression tests use fake timers and do not add real wall-clock delay.

Related: [#13] covered configurable retry policy broadly and was closed; this report is the concrete Gemini metadata case.

Related

Tickets: #108
Tickets: #119
Tickets: #13

Discussion


Log in to post a comment.