Skip to content

fix(provider): retry transient errors on first stream item before ending turn - #10968

Merged
alexhancock merged 6 commits into
aaif-goose:mainfrom
Abhijay007:fix/retry-transient-first-stream-item
Aug 10, 2026
Merged

fix(provider): retry transient errors on first stream item before ending turn#10968
alexhancock merged 6 commits into
aaif-goose:mainfrom
Abhijay007:fix/retry-transient-first-stream-item

Conversation

@Abhijay007

Copy link
Copy Markdown
Collaborator

Fixes: #10887

Summary

Some providers on OpenRouter (poolside being a common one) frequently hit 429s that show up as the first item in the response stream, after the connection itself succeeds. The existing retry logic only covers the HTTP call, so goose was treating these as hard failures and ending the turn.

Fix peeks the first stream item before passing the stream back. Transient errors get retried with backoff;
non-transient errors and anything after the first successful chunk pass through as-is. Providers that manage their own
context are skipped to avoid replaying state.

Testing

Verification: cargo test -p goose --lib -- agents::reply_parts::tests::first_item
agents::reply_parts::tests::empty_stream agents::reply_parts::tests::error_after — 7 tests, all passing.

Douwe M Osinga added 2 commits August 10, 2026 20:03
@DOsinga

DOsinga commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Thanks for starting this, @Abhijay007! I updated your branch with current main and adjusted the implementation to keep first-item polling lazy, so cancellation in the state-machine loop still works while it waits. Stream-creation retries remain provider-local, and the outer retry only handles transient errors from the first stream item. I also added focused coverage for recovery, exhaustion, opt-out, replacement creation failures, post-first-item errors, empty streams, and lazy return behavior.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4d7cdb4a24

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +435 to +436
if !skip_backoff {
tokio::time::sleep(delay).await;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Make the retry backoff cancellable

When the first stream item is a transient error, this sleep prevents the legacy agent loop from responding to cancellation until the entire backoff finishes, because crates/goose/src/agents/agent.rs:2466-2468 awaits stream.next() before checking its cancellation token. A provider-supplied retry_delay can make Stop appear hung for an arbitrarily long time, while the state-machine path remains cancellable through the tokio::select! in ops_llm.rs:513-518; make this retry wait cancellation-aware in both agent-loop paths.

AGENTS.md reference: AGENTS.md:L19-L23

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fc66ca562f

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +415 to +416
if goose_providers::retry::should_retry(&error, &retry_config)
&& attempts < retry_config.max_retries =>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid retrying permanent first-frame 4xx errors

When an OpenAI-compatible gateway sends a deterministic client/validation failure as the first SSE frame, this retry check treats it as transient because the existing parser maps choice-less status/statusCode/code >= 400 and detail error frames to ProviderError::ServerError (crates/goose-provider-types/src/formats/openai.rs:1113-1137). That means a bad model/payload/auth-shaped in-stream 4xx now replays the same invalid request until max_retries and delays surfacing the real error; discriminate 4xx/validation frames before applying the transient retry policy.

Useful? React with 👍 / 👎.

@alexhancock
alexhancock added this pull request to the merge queue Aug 10, 2026
@alexhancock
alexhancock removed this pull request from the merge queue due to a manual request Aug 10, 2026
@alexhancock
alexhancock merged commit b7ddf93 into aaif-goose:main Aug 10, 2026
25 checks passed
michaelneale added a commit that referenced this pull request Aug 10, 2026
* origin/main:
  fix(mcp): prune dead notification subscribers (#11032)
  chore: remove the extension and tool count suggestion (#10869)
  feat: compaction in the GDK (#11042)
  fix(provider): retry transient errors on first stream item before ending turn (#10968)
  feat(cli): add /new to start a fresh session without restarting (#10767)
  feat(acp): title new sessions from _meta.sessionTitle (#10712)
  fix: adjust rmcp::model::Meta ref (#11107)
  Skip hook loading and lifecycle events for subagents (#10596)
  Sanitize Unicode tags in Responses output (#10745)
  fix(conversation): sanitize nested tool responses (#10609)
  fix(hints): bound recursive file expansion (#10546)
  fix(providers): drop stale signed thinking blocks after a mid-conversation model switch (#10007)
  fix(desktop): clarify compact cost display (#11093)
  Index messages by (session_id, created_timestamp, id) to stop on-disk sort storms (#10874)
  docs: add tool shim guide covering when to enable, backends, and troubleshooting (#10858)
  fix(deep-link): route extension/session deep links to regular windows not standalone app windows (#10908)
  fix(ui): raise chat input z-index so slash menu appears above loading indicator (#11015)
  fix(ui): support remote working directory for external backend (#10827)
lifeizhou-ap added a commit that referenced this pull request Aug 11, 2026
* main:
  fix(mcp): prune dead notification subscribers (#11032)
  chore: remove the extension and tool count suggestion (#10869)
  feat: compaction in the GDK (#11042)
  fix(provider): retry transient errors on first stream item before ending turn (#10968)
  feat(cli): add /new to start a fresh session without restarting (#10767)
  feat(acp): title new sessions from _meta.sessionTitle (#10712)
  fix: adjust rmcp::model::Meta ref (#11107)
  Skip hook loading and lifecycle events for subagents (#10596)
  Sanitize Unicode tags in Responses output (#10745)
  fix(conversation): sanitize nested tool responses (#10609)
  fix(hints): bound recursive file expansion (#10546)
  fix(providers): drop stale signed thinking blocks after a mid-conversation model switch (#10007)
  fix(desktop): clarify compact cost display (#11093)
  Index messages by (session_id, created_timestamp, id) to stop on-disk sort storms (#10874)
  docs: add tool shim guide covering when to enable, backends, and troubleshooting (#10858)
  fix(deep-link): route extension/session deep links to regular windows not standalone app windows (#10908)
  fix(ui): raise chat input z-index so slash menu appears above loading indicator (#11015)
  fix(ui): support remote working directory for external backend (#10827)
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.

Automatically retry failed API requests

3 participants