Skip to content

fix(sdk): stop killing agent runs on a 180s silent tool call - #6982

Open
Aman-goel-04 wants to merge 4 commits into
Agenta-AI:mainfrom
Aman-goel-04:fix/tool-call-taking-gt-180-secs-kills-the-run-6976
Open

Aman-goel-04 wants to merge 4 commits into
Agenta-AI:mainfrom
Aman-goel-04:fix/tool-call-taking-gt-180-secs-kills-the-run-6976

Conversation

@Aman-goel-04

@Aman-goel-04 Aman-goel-04 commented Sep 20, 2026

Copy link
Copy Markdown
Contributor

Fixes #6976

Summary

A tool call that stayed silent for more than 180 seconds killed the whole agent run with a generic "agent run failed", even though the runner has much wider limits (30 min idle, 30 min per tool call).

Root cause: SandboxAgentBackend defaulted AGENTA_RUNNER_TIMEOUT_SECONDS to 180 with its own literal, overriding the wider default in ts_runner.py. httpx applies that float as a per-read idle timeout on the NDJSON stream, so any 180s gap between frames raised httpx.ReadTimeout. That exception has an empty message, so the wire layer fell back to "agent run failed".

Changes:

  • One shared AGENT_DEFAULT_TIMEOUT (1920s, above the runner's 30 min limits so the runner always trips first), exported from agenta.sdk.agents.utils and used by the adapter and the fake test backend. The duplicate 180 literal is gone.
  • deliver_http_stream now catches httpx.ReadTimeout and raises a named "Agent runner stream stalled: no record for Ns" error, matching the subprocess transport's wording.
  • Updated the four docker-compose env examples, the self-host configuration docs, and the design docs to the new default.

Trade-off: a genuinely dead runner connection now takes about 32 minutes to give up on instead of 3. This is the runner-as-authority design, where the runner emits a terminal record when its own limits trip.

Not included: a runner-side heartbeat frame during silent tool calls (a larger runner change).

Testing

Verified locally

  • pytest oss/tests/pytest/unit/agents/test_runner_transport_timeout.py: 6 passed.
  • ruff format --check is clean on the changed Python files.

Added or updated tests

  • ReadTimeout in the HTTP stream produces the named "stalled" error.
  • The adapter's default timeout equals the shared constant, so the two cannot drift again.
  • The shared default is wider than the runner's 30 minute idle limit.

QA follow-up

N/A

Demo

main:
Screenshot 2026-09-20 at 1 52 27 PM

this branch:
Screenshot 2026-09-20 at 1 52 58 PM

Checklist

  • Demo shows the real app running this branch (not a mock-up or recreated UI), or is marked N/A
  • Relevant tests pass locally
  • Relevant linting and formattin
  • I have signed the CLA, or I will sign it when the bot prompts me

Copilot AI lite review requested due to automatic review settings September 20, 2026 08:04
@github-actions

github-actions Bot commented Sep 20, 2026

Copy link
Copy Markdown
Contributor

✅ Thanks @Aman-goel-04! This PR now meets the contribution requirements and has been reopened. A maintainer will review it soon.

@github-actions github-actions Bot added the incomplete-pr PR is missing required template sections or a demo recording label Sep 20, 2026
@github-actions github-actions Bot closed this Sep 20, 2026
@coderabbitai

coderabbitai Bot commented Sep 20, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

Understand this PR’s impact

Explore downstream dependencies and potential security impact with Blast Radius.

View blast radius →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Advanced

Run ID: 8ba8f17f-aa2f-4e08-ad3f-a4da37556c68

📥 Commits

Reviewing files that changed from the base of the PR and between 9a3d336 and 977762f.

📒 Files selected for processing (17)
  • docs/docs/self-host/reference/01-configuration.mdx
  • hosting/docker-compose/ee/env.ee.dev.example
  • hosting/docker-compose/ee/env.ee.gh.example
  • hosting/docker-compose/oss/env.oss.dev.example
  • hosting/docker-compose/oss/env.oss.gh.example
  • sdks/python/agenta/sdk/agents/adapters/sandbox_agent.py
  • sdks/python/agenta/sdk/agents/utils/__init__.py
  • sdks/python/agenta/sdk/agents/utils/ts_runner.py
  • sdks/python/oss/tests/pytest/integration/agents/_fake_runner_backend.py
  • sdks/python/oss/tests/pytest/unit/agents/test_runner_transport_timeout.py
  • web/mobile/src/features/chat/LiveConversation.tsx
  • web/oss/src/components/AgentChatSlice/AgentConversation.tsx
  • web/packages/agenta-chat/src/hooks/useAgentConversation.ts
  • web/packages/agenta-chat/src/hooks/useServerSessionInputs.ts
  • web/packages/agenta-chat/src/model/error.ts
  • web/packages/agenta-chat/tests/unit/hooks/useServerSessionInputs.test.ts
  • web/packages/agenta-chat/tests/unit/model/sendRefusal.test.tsx
🚧 Files skipped from review as they are similar to previous changes (5)
  • hosting/docker-compose/oss/env.oss.gh.example
  • hosting/docker-compose/ee/env.ee.dev.example
  • hosting/docker-compose/ee/env.ee.gh.example
  • docs/docs/self-host/reference/01-configuration.mdx
  • hosting/docker-compose/oss/env.oss.dev.example

Included review availability: Your plan provides up to 8 included reviews per hour; 6 remain after this review.


📝 Summary

Summary by CodeRabbit

  • New Features

    • Late send refusals now display the specific reason provided by the server.
    • Stream failures provide clearer “stalled” messages while keeping internal commands and addresses out of user-facing errors.
  • Improvements

    • Agent runner idle timeout defaults increased to 32 minutes for longer-running tasks.
    • Timeout settings are now consistent across runner transports, configuration examples, and documentation.
  • Bug Fixes

    • Error details from failed or refused sends are preserved when messages are returned to the composer.

Walkthrough

The PR raises the shared runner timeout to 1920 seconds, aligns related documentation and environment examples, reports stalled stream errors without internal details, and propagates pre-admission failure reasons into chat refusal messages.

Changes

Runner timeout handling

Layer / File(s) Summary
Shared timeout contract
sdks/python/agenta/sdk/agents/utils/..., sdks/python/agenta/sdk/agents/adapters/..., sdks/python/oss/tests/pytest/integration/agents/...
The SDK exports AGENT_DEFAULT_TIMEOUT at 1920 seconds and uses it for transport and sandbox backend defaults.
Stream timeout reporting
sdks/python/agenta/sdk/agents/utils/ts_runner.py, sdks/python/oss/tests/pytest/unit/agents/test_runner_transport_timeout.py
HTTP read timeouts and subprocess stalls produce explicit stalled-stream errors. Tests verify that internal URLs and commands are not exposed.
Configuration and documentation alignment
docs/design/agent-workflows/..., docs/docs/self-host/..., hosting/docker-compose/...
Timeout examples and documented defaults change to 1920 seconds.

Chat refusal reason handling

Layer / File(s) Summary
Stream failure reason extraction
web/packages/agenta-chat/src/hooks/useServerSessionInputs.ts, web/packages/agenta-chat/tests/unit/hooks/useServerSessionInputs.test.ts
Error frames provide trimmed failure text to onFailed for runs that fail before admission.
Refused-send recovery contract
web/packages/agenta-chat/src/hooks/useAgentChatQueue.ts, web/packages/agenta-chat/src/hooks/useAgentConversation.ts, web/packages/agenta-chat/tests/unit/hooks/useAgentChatQueue.test.ts
Recovery callbacks accept and forward an optional refusal reason.
User-facing refusal messages
web/packages/agenta-chat/src/model/error.ts, web/mobile/src/features/chat/LiveConversation.tsx, web/oss/src/components/AgentChatSlice/AgentConversation.tsx, web/packages/agenta-chat/tests/unit/model/sendRefusal.test.tsx
Late-refused sends use the supplied reason, with existing fallback wording when no reason is available.

Priority: ➖ Normal

Estimated code review effort: 4 (Complex) | ~45 minutes

Severity of issue fixed: Medium

Sequence Diagram(s)

sequenceDiagram
  participant Runner
  participant deliver_http_stream
  participant SandboxAgentBackend
  Runner->>deliver_http_stream: emit stream records
  deliver_http_stream->>SandboxAgentBackend: report completed or stalled stream
  SandboxAgentBackend->>Runner: return result or transport error
Loading
sequenceDiagram
  participant RunStream
  participant useServerSessionInputs
  participant useAgentChatQueue
  participant LiveConversation
  RunStream->>useServerSessionInputs: send error frame
  useServerSessionInputs->>useAgentChatQueue: call onFailed(reason)
  useAgentChatQueue->>LiveConversation: restore refused send with reason
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.22% which is insufficient. The required threshold is 60.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 18 functions across 15 files. (5 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the primary SDK fix: preventing agent runs from terminating after a 180-second silent tool call.
Description check ✅ Passed The description directly explains the timeout issue, root cause, implementation changes, testing, and related web error handling.
Linked Issues check ✅ Passed The PR satisfies the coding requirements in [#6976]. AGENT_DEFAULT_TIMEOUT is the shared 1,920-second default for the adapter, transports, and fake backend. Tests assert that the adapter uses this c…
Out of Scope Changes check ✅ Passed The changes remain within [#6976]. Transport, adapter, fake-backend, configuration, documentation, and regression-test changes implement the timeout and stall-reporting requirements. The web changes p…
Full details: Docstring Coverage

Explanation

Docstring coverage is 22.22% which is insufficient. The required threshold is 60.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 18 functions across 15 files. (5 skipped: 5 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI 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.

Copilot review overview

🟢 Approval recommended

The changes are tested and no blocking issues remain; one documentation nit is non-blocking.

Review effort: Lite
Findings: None

What changed in this PR

Updates SDK runner timeouts to prevent silent tool calls from failing after 180 seconds and improves stalled-stream diagnostics.

Changes:

  • Centralizes the 1920-second default timeout.
  • Adds explicit HTTP stream stall errors.
  • Updates tests, deployment examples, and documentation.
File Summary
sdks/​python/​oss/​tests/​pytest/​unit/​agents/​test_runner_transport_timeout.py Adds timeout regression coverage.
sdks/​python/​oss/​tests/​pytest/​integration/​agents/​_fake_runner_backend.py Aligns fake backend timeout defaults.
sdks/​python/​agenta/​sdk/​agents/​utils/​ts_runner.py Defines shared timeout and handles stream timeouts.
sdks/​python/​agenta/​sdk/​agents/​utils/​__init__.py Exports the shared timeout constant.
sdks/​python/​agenta/​sdk/​agents/​adapters/​sandbox_agent.py Uses the shared timeout default.
hosting/​docker-compose/​oss/​env.oss.gh.example Updates timeout example.
hosting/​docker-compose/​oss/​env.oss.dev.example Updates timeout example.
hosting/​docker-compose/​ee/​env.ee.gh.example Updates timeout example.
hosting/​docker-compose/​ee/​env.ee.dev.example Updates timeout example.
docs/​docs/​self-host/​reference/​01-configuration.mdx Updates configuration documentation.
docs/​design/​agent-workflows/​projects/​runner-interface/​README.md Updates runner timeout documentation.
docs/​design/​agent-workflows/​interfaces/​in-service/​backend-adapter.md Updates adapter documentation.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@github-actions github-actions Bot removed the incomplete-pr PR is missing required template sections or a demo recording label Sep 20, 2026
@github-actions github-actions Bot reopened this Sep 20, 2026
@mmabrouk

Copy link
Copy Markdown
Member

qa found one issue on 329b24f.

the primary fix works: a silent shell tool completed after 181 seconds instead of being cut off at the old 180-second boundary. video proof: https://www.browserbase.com/sessions/0e8687c2-990d-4681-a4e4-5a89f47d0772

the new stall detail does not reach the user. with a 3-second test timeout, services logged `Agent runner stream stalled: no record for 3.0s`, but the browser only showed `Message wasn't sent — try again.` video proof: https://www.browserbase.com/sessions/db9602e1-6df2-4ed3-9653-c653e4c45d27

please make the sanitized stream error visible in the failed turn and add regression coverage for that path.

Copilot AI review requested due to automatic review settings September 21, 2026 16:20

Copilot AI 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.

Copilot review overview

🔵 Needs a closer look

The user-facing timeout message exposes the internal runner URL; remove the URL from that message.

Review effort: Lite
Findings: None

@Aman-goel-04
Aman-goel-04 force-pushed the fix/tool-call-taking-gt-180-secs-kills-the-run-6976 branch from 9a3d336 to 977762f Compare September 21, 2026 16:29
Copilot AI review requested due to automatic review settings September 21, 2026 16:29

Copilot AI 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.

Copilot review overview

🟢 Approval recommended

The reviewed changes are complete; the remaining documentation comment is a minor nit.

Review effort: Lite
Findings: None

@Aman-goel-04

Copy link
Copy Markdown
Contributor Author

Thanks for the QA. Found it: with the 3s timeout the stall hits before the runner admits the turn, so the web treats the error frame as a refused send and throws away its text. That's why you only saw "Message wasn't sent".

What I changed:

  • useServerSessionInputs.ts: the error frame's errorText (from error or data-agent-error) is now kept and passed to onFailed(reason).
  • useAgentChatQueue.ts and useAgentConversation.ts: restoreRefusedSend now receives that reason.
  • model/error.ts: new lateRefusedSendRejections(), built on the same wording as describeRefusedSend, used by AgentConversation.tsx (desktop) and LiveConversation.tsx (mobile). The composer now shows "wasn't sent — Agent runner stream stalled: no record for 3.0s", and keeps the old "try again" line when there's no text.
  • ts_runner.py: since that text now reaches users, I also took Copilot's note and moved the runner URL and command line out of the stall message. They go to the log through _transport_error instead.

Tests: the reason is passed on for a pre-turn error (both frame types), no reason when the frame has no text, and an error after the turn id still goes to the transcript. The queue forwards the reason to the composer, lateRefusedSendRejections is covered in sendRefusal.test.tsx, and the timeout tests check the URL and command stay out of the message.

I also merged main, since the branch had conflicts.

Two questions:

  1. No turn exists yet in this case, so I put the reason in the composer note. Did you want it in the transcript instead?
  2. Right now any error before admission shows its own text, not just stalls. Should I limit it to stalls?

I couldn't reproduce it in the browser, so it's only covered by unit tests. Could you re-run your 3s scenario when you get a chance?

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.

(bug) A tool call that stays silent for more than 180 seconds kills the agent run with "agent run failed"

3 participants