[Bug] Subagent delegation lacks timeout, monitoring, and abort controls—caused 12+ hour session hang

Status Closed — not planned
Maintainer reply None cached
Activity 12 comments · opened May 22, 2026 · closed Jul 6, 2026

Bug Description
Title: Subagent delegation has no timeout, no monitoring, and no sanity checks — caused multi-hour session failure

During a session involving GitHub Actions workflow review, plan mode was activated. Claude Code was asked to verify a claim it had made about a specific GitHub Action.
This required a single web search that would have completed in under 10 seconds via a direct WebFetch call.

Critical Issue - Zero sanity checks on subagent execution:

When Claude Code spawns a subagent, there is no mechanism available to the user or to Claude Code itself to bound, monitor, or abort that subagent's execution.
Specifically:

  • There is no user-configurable timeout. Users cannot set a parameter such as "this subagent task should not exceed 60 seconds."
  • The main Claude Code process has no check-in interval. It simply waits for the subagent to return with no periodic status reporting.
  • There is no abort trigger. Once a subagent is spawned, the user cannot cancel it.
  • There is no progress visibility. The user has no way to see what the subagent is currently doing or how far along it is.
  • The subagent starts with zero knowledge of Claude Code's prior conversation context, including the specific claim or suggestion that prompted the search. This means

the subagent cannot self-correct or stay anchored to the original intent — it only knows what Claude Code wrote in the prompt it was handed.

A subagent will run until it completes its task or exhausts its own context limit. In this session, a subagent tasked with a single web lookup instead made 10+
sequential web fetches because Claude Code wrote an over-scoped prompt and there was nothing — no timeout, no check-in, no guardrail — to catch or stop it. The user had
no ability to detect the problem or intervene. The session ran over 12 hours.

This is a fundamental reliability gap. Users have no way to "ground" a subagent in terms of runtime expectations, and Claude Code has no way to self-detect that a
delegated task has gone off the rails.

Secondary Issue - Plan mode forces subagent delegation with no user transparency:

Plan mode in Claude Code requires ALL research tasks to be routed through Explore subagents. Claude Code cannot make direct tool calls such as WebFetch while plan mode
is active. When this constraint forces a task to be slower or more indirect than it would otherwise be, Claude Code does not inform the user. The user has no visibility
into why something is taking long and no opportunity to exit plan mode before the delegation occurs.

This constraint exists for legitimate reasons — large codebase exploration benefits from subagent isolation. But applying it to a clearly bounded single-URL lookup is a
structural mismatch, and the user has no way to know it is happening.

What Anthropic should review:

  • Implement user-configurable subagent timeouts — the single most important missing control
  • Implement a check-in mechanism — if a subagent exceeds a time threshold, surface a warning to the user with an option to abort
  • Require Claude Code to disclose upfront when a mode constraint is forcing subagent delegation instead of a direct tool call, and what the user's options are
  • Evaluate whether plan mode's subagent requirement should apply to clearly bounded single-tool operations
  • Consider whether subagents should receive a summary of the conversation context that prompted them, so they can stay anchored to the original intent

Impact: User lost 12+ hours of a session to what should have been a 30-minute task. The absence of subagent sanity checks is a significant operational gap that will affect any sufficiently long or complex delegated task. In time-sensitive or higher-stakes contexts this is a serious reliability and safety concern.

View original on GitHub ↗

12 Comments

github-actions[bot] · 3 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/37521
  2. https://github.com/anthropics/claude-code/issues/41461
  3. https://github.com/anthropics/claude-code/issues/52492

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

jshaofa-ui · 3 months ago

Proposed Solution: Subagent Delegation Lacks Timeout/Monitoring/Abort

Root Cause

When the main agent delegates to a subagent:

  1. No timeout configured — subagent can run indefinitely
  2. No monitoring — no heartbeat, progress tracking, or health check
  3. No abort controls — user cannot cancel a stuck subagent
  4. No sanity checks — no validation that subagent is making progress

This caused a 12+ hour hang during a simple web search task.

Proposed Fix

File: src/agents/delegation.ts — Add timeout configuration:

interface SubagentConfig {
  timeout?: number;        // Default: 5min
  idleTimeout?: number;    // Default: 1min
  maxRetries?: number;     // Default: 1
  monitorInterval?: number;// Default: 5s
}

async function delegateToSubagent(task, config = {}) {
  const timeoutPromise = new Promise((_, reject) => {
    setTimeout(() => reject(new SubagentTimeoutError(...)), config.timeout);
  });
  return Promise.race([this._executeSubagent(task, config), timeoutPromise]);
}

File: src/agents/monitor.ts — Add heartbeat monitoring:

// Check every 5s: if no heartbeat for 30s → emit 'stalled'
// If no output for 60s → emit 'idle' warning

Key Changes

  • src/agents/delegation.ts: Add timeout + idle timeout + retry config
  • src/agents/monitor.ts: Heartbeat monitoring + idle detection
  • src/agents/control.ts: User-facing /abort <subagent-id> command
  • src/agents/delegation.ts: Progress reporting interface

Full solution: solutions/claude-code-61405-subagent-delegation-timeout-monitoring-fix.md

yurukusa · 3 months ago

Posting this as one signal among many — this issue is sub-pattern 3 in a cluster of six sub-agent issues filed over the past 5 days that share an observability axis. Wrote up an independent analysis at https://gist.github.com/yurukusa/9857a9ed407696ba8483b354917ff161 decomposing them into four sub-patterns: dispatch fabrication (#61107, #61167), silent stall (#61315, #60987), absence of observation and control (this issue), and scope expansion (#61102).
The structural point relevant here: this is the sub-pattern that does not admit a complete operator-side defense, because the failure mode is the absence of a primitive (timeout/abort/progress), not the misuse of one. The other three sub-patterns can be partially closed with hooks (cc-safe-setup PRs #264, #275, #281, #286); this one's only operator-side workaround is a wall-clock alarm that surfaces a visible alert + manual kill instructions after a configurable threshold. The real fix lands at the harness layer.
Filing as a structural cluster (rather than four separate dups) matters because Anthropic's duplicate-detection bot collapses on lexical overlap and would not derive the observability axis from the issue texts themselves.
Disclosure at the bottom of the Gist; relevant short version: yurukusa author of cc-safe-setup (MIT, free) and two paid books (Claim-Verify Handbook, Postmortems) where the Appendix D corpus this analysis sits next to lives.

yurukusa · 3 months ago

Shipped an operator-side proxy for the absent timeout primitive: yurukusa/cc-safe-setup#298 (examples/dispatch-liveness-watchdog.sh, 28/28 tests passing).
Scope honesty. This does not fix the underlying issue (the Agent tool has no timeout / monitoring / abort primitive). It makes the symptom observable at a point the operator already looks (the next UserPromptSubmit) so a hung dispatch surfaces before the operator has to notice that nothing is happening. The harness fix has to land on the Agent-tool layer.
How it works. One hook file, three event registrations:

  • PreToolUse + Agent matcher: writes ~/.claude/state/dispatch-watchdog/<session>/<unix-ts>-<rand> with a short dispatch summary.
  • PostToolUse + Agent matcher: removes the oldest state file in the session directory (FIFO).
  • UserPromptSubmit + "" matcher: lists state files older than CC_DISPATCH_WATCHDOG_THRESHOLD_SEC (default 1800 = 30m), emits an advisory naming each one (wall-clock elapsed + summary).

Advisory text quotes the three operator-side options at the moment a hang is surfaced: wait if plausibly still progressing, identify the OS-level process and SIGKILL (with the loss-of-parent-state caveat noted explicitly), or raise the threshold if legitimate dispatches normally run longer.
Limits I'd flag: out-of-order completion retires the wrong state file (the FIFO assumption); this keeps the count of in-flight dispatches correct but can mislabel which specific dispatch is the stale one. Per-dispatch identity tracking would require harness cooperation we don't have. Also: in strict mode (opt-in via CC_DISPATCH_WATCHDOG_MODE=strict) the watchdog blocks the next prompt with exit 2, which is disruptive — default is advisory only.
This sits at sub-pattern 3 in the four-sub-pattern decomposition I wrote up at https://gist.github.com/yurukusa/9857a9ed407696ba8483b354917ff161 — the one sub-pattern in that cluster where the operator side can only surface the symptom; the real fix is the timeout primitive you asked for in this issue.

meefs · 3 months ago

Follow-up after reviewing the docs with Claude 4.7 — wanted to refine the asks now that I've checked what's already in place for subagents.

What's already there (and isn't being applied):

  • maxTurns already provides a tool-call budget per subagent ("Maximum number of agentic turns before the subagent stops"). The problem is that:
  1. it's set per-subagent-definition rather than per-invocation,
  2. it isn't exposed for the built-in Explore, Plan, and general-purpose subagents (which is what gets used when there's no custom subagent in play), and
  3. there's no settings.json key to set a sane default for the built-ins.

My 12-hour session ran through the Plan subagent — which has no user-visible turn cap at all.

  • /agents → Running tab provides live observability and a stop control. Useful, but it's pull-based. If you don't know to open it, you don't see the runaway. No push notification when a subagent exceeds a threshold.
  • SubagentStart / SubagentStop hooks exist, plus per-subagent PreToolUse / PostToolUse hooks. The lifecycle plumbing is already there — just not surfaced as first-class user controls.
  • BASH_DEFAULT_TIMEOUT_MS and BASH_MAX_TIMEOUT_MS already give users timeout control over individual Bash tool calls via settings.json. The pattern for what I'm asking for already exists at the Bash layer — it just doesn't extend up to the subagent lifecycle.

Related: #25569 reports that CLAUDE_CODE_MAX_OUTPUT_TOKENS isn't respected by subagents — they ignore the setting and use a hardcoded 32K. That's evidence of a recurring architectural pattern: subagents are a layer where existing user-facing controls quietly stop applying. My session is a stronger version of the same shape.

Why user-settable controls are required:

A subagent can't reliably know its own progress without a sharply defined success criterion baked into the delegation. Self-introspection at "% complete" is not something LLMs do well without additional tooling/metering, and the original issue shouldn't be read as asking for that. Because the agent can't reliably self-bound, the user has to be able to set the bounds of the runtime. Tool-call count is the unit the agent can reason about; a global, local, and project based user controlled timeout is the missing control here. Both belong in settings.json, and neither is sufficient alone — maxTurns doesn't fire if a single tool call hangs on a slow MCP or rate-limited fetch.

Concrete asks, each individually filable:

  1. User defined subagent timeout in settings.json — e.g., subagentMaxRuntimeMs, with optional override in subagent frontmatter. Bash already has this pattern; subagents should match. Single highest-leverage change.
  2. Expose maxTurns for built-in subagents (Explore, Plan, general-purpose) and allow defaults in settings.json. The control exists but doesn't reach where it's needed.
  3. Per-invocation maxTurns override so the parent agent can scale the budget to the task. A single-URL fetch should get 2–3 turns; a codebase exploration can have 20. The current static-per-definition model can't make that distinction.
  4. Push-based long-running warning — banner or chime when any subagent exceeds a configurable threshold, prompting the user to inspect or abort. The /agents Running tab already has the data; surface it actively.
  5. Plan mode bounded-operation exception — permit narrowly-scoped direct tool calls (single WebFetch to a specific URL, single Read of a specific file, single WebSearch with a specific query) without forcing them through the Plan subagent. The mode-level constraint is a blunt instrument; that's the structural mismatch that produced this issue.

Happy to provide any additional detail that will resolve this lack of controls for Claude Code sub-agents. A simple timeout setting that I could set for all sub-agents would have prevented this.

yurukusa · 3 months ago

@meefs — the "subagents are a layer where existing user-facing controls quietly stop applying" framing is the cleanest articulation of the structural pattern I've seen. Three independent corroborations of that shape, then the operator-side substrate I shipped yesterday and where I think Ask #1 lands hardest.
The pattern at the budget axis is a cluster, not a single point. Three independent controls already exist at the parent layer and do not propagate across the subagent boundary:

  1. BASH_DEFAULT_TIMEOUT_MS / BASH_MAX_TIMEOUT_MS — reaches the Bash tool, does not extend to the subagent runtime that contains Bash calls (the gap you named).
  2. CLAUDE_CODE_MAX_OUTPUT_TOKENS — reaches the harness, hardcoded to 32K at the subagent (#25569, closed on 2026-02 but the architectural pattern stands).
  3. maxTurns — exists in subagent frontmatter, not exposed for the built-in Explore/Plan/general-purpose subagents that get used when no custom subagent is in play.

Three controls, three different axes (wall-clock, token budget, tool-call budget), same shape: the user-articulated budget lives at the parent layer and stops at the subagent boundary. The fix surface is the same in all three cases — settings.json has to carry through.
This is the budget-axis variant of the broader recognition-without-arrest cluster. The cluster organizing on #60226 catalogs claim-verify gaps at the verification axis — where the verification step would have to read from a different source than the claim derives from, and the verification step does not exist. Your case is the control-propagation axis: the user-articulated control surface exists, but does not extend across the boundary where it would need to apply. Same architectural inversion at a different cross-section. Worth surfacing because the eventual platform fix should consider both axes together — the boundary is the unit, not the individual control.
Operator-side substrate available today (scope-honest). I shipped examples/dispatch-liveness-watchdog.sh in yurukusa/cc-safe-setup#298 (28/28 tests passing) on 2026-05-22 — a PreToolUse(Agent) + PostToolUse(Agent) + UserPromptSubmit triplet that writes per-dispatch state files and surfaces hung dispatches at the next operator turn with wall-clock-elapsed advisories. What it does: makes the symptom observable at a point the operator already looks (your next prompt submission), so a runaway surfaces before the operator has to remember to open /agents → Running. What it does not do: it cannot abort the dispatch. The harness has to expose that primitive — a hook can observe, but it cannot send a SIGTERM-equivalent into a running subagent because the harness doesn't expose that affordance. This is the half-substrate; the harness fix you're asking for is the other half.
The 2026-06-15 billing reclassification adds urgency to Ask #1. Anthropic's split routes --print invocations to a separate "programmatic" credit bucket. A subagent that runs for 12 hours unbounded today is a clock-time problem; after 2026-06-15 it's also a direct $/turn cost problem — a runaway can burn through the programmatic bucket while the user is asleep with no abort surface. subagentMaxRuntimeMs becomes a financial control in 23 days, not just a reliability control. That changes the priority ordering on your asks: #1 (settings-level runtime cap) clears the failure mode in your report and the post-2026-06-15 cost-exposure mode at the same time, with the minimum platform change.
Specific endorsement on the five asks: #1 + #3 + #4 is the operationally complete set. #1 sets the global default (settings.json carries the boundary), #3 lets the parent agent scale per-task (WebFetch 2–3 turns vs. codebase exploration 20+), #4 surfaces the threshold breach push-based so the operator doesn't have to remember to look. #2 (expose maxTurns for built-ins) is a strict subset of #3 and lands as a side effect. #5 (plan-mode bounded-operation exception) is the right separate ask — the mode-level constraint is structurally distinct from the subagent-budget cluster and earns its own thread.
Disclosure: I'm yurukusa; I write commercial material adjacent to this territory (Migration Playbook, Claim-Verify Handbook, Safety Lab) and ship the MIT-licensed cc-safe-setup hook collection that PR #298 belongs to. The structural argument and the cross-references to #60226 / #25569 stand independently of either.

waitdeadai · 3 months ago

@meefs @yurukusa — one measurement-axis addendum to the control-propagation framing.

The verification-axis surface (#60226) and the control-propagation axis you've named here are structurally parallel but produce inverse evidence forms, and that asymmetry affects the measurement substrate design.

Verification-axis failures produce positive evidence — fabricated receipts, false tool-call reports — that a measurement hook can detect by checking whether the asserted action occurred at the event boundary. The detection grammar has something to match against.

Control-propagation failures produce negative evidence — a constraint that should have applied didn't apply — and are detectable only when the unconstrained subagent either (a) times out visibly, (b) emits more output than the parent-layer budget would have permitted, or (c) takes an action blocked at the parent layer but not propagated. PR #298's dispatch-liveness-watchdog surfaces case (a). Cases (b) and (c) have no operator-side observable today because the harness doesn't expose the post-subagent budget-consumed field in a hook-readable form.

The 2026-06-15 billing reclassification changes case (b)'s measurability: the gap between parent-layer budgeted programmatic spend and actual credit consumed becomes a billing artifact. That's the first operator-accessible proxy for unmeasured subagent overruns — not a real-time control, but an after-the-fact measurement substrate for auditing whether deployed subagents are running within parent-layer budget intent. An operator who wants to quantify the control-propagation failure rate before 2026-06-15 has no clean signal; an operator who runs an audit cohort after 2026-06-15 gets the credit-differential proxy for free, as a billing side effect.

Worth surfacing to Anthropic as a minimal ask alongside asks #1–#5: expose programmatic credit consumed per-subagent in the API response or a hook-readable artifact. That turns the billing side effect into a first-class measurement primitive without requiring harness changes to the abort surface.

yurukusa · 3 months ago

@waitdeadai — accepting the inverse-evidence-form asymmetry as a structural property of the cluster catalog and the Ask #6 proposal as a measurement primitive worth surfacing alongside @meefs's Asks #1–#5. Three responses then the propagation surface.

On the positive-vs-negative evidence asymmetry as a catalog axis. The framing is sharper than the recognition-without-arrest cluster's existing organizing principle and earns its own annotation column in the cluster catalog. The cluster has been organizing on the verification-axis (gate-absent at the lifecycle event), which the receipts substrate measures cleanly because the failure produces a detectable artifact — the fabricated narrative is in the transcript, the missing tool call is in the session JSONL, and the cross-claim mismatch is computable at the next lifecycle event. The control-propagation axis you've named here has no detectable artifact at the failure moment by construction — the constraint that should have applied left no trace of its absence, only the downstream consequences (timeout, over-budget output, or unconstrained tool action) make the failure observable. Two axes, two evidence forms, one measurement-substrate-design implication: a catalog row needs to be tagged with its evidence form so the operator picking up a defense knows whether they're looking for a detection grammar (positive) or a constraint-propagation primitive plus a downstream measurement proxy (negative).

I'll add an evidence_form column to the Matrix Gist in the 72h propagation window — values positive (verification-axis, gate-absent surface; PRs #282 / #283 / #285 / #286 / #289 / #296 / #297 family) and negative (control-propagation axis; PR #298 dispatch-liveness-watchdog as the case (a) operator-side observable). The asymmetry then surfaces at the column level for any future reader and the §6.2 sub-table placement criterion stays orthogonal — a row earns §6.2 because its decomposition's denominator is partially out-of-process, independent of which evidence form the row falls under.

On PR #298 as the case (a) operator-side observable, and cases (b) and (c) as the unsolved control-propagation surface today. The mapping is exact and clarifies the scope of what PR #298 covers vs. what remains unaddressable from the operator side. Dispatch-liveness-watchdog surfaces case (a) — visible timeout becomes a UserPromptSubmit advisory at the next event boundary — because the parent-layer can observe the absence of the expected dispatch-end receipt within the timeout window. Cases (b) (subagent emits more output than parent-layer budget would permit) and (c) (subagent takes action blocked at parent layer but not propagated) have no parent-layer observable because the harness doesn't expose the post-subagent budget-consumed or post-subagent tool-call-list fields in a hook-readable form. Articulating that boundary honestly in PR #298's description matters — the hook is the case (a) surface, not the full control-propagation defense, and operators relying on it shouldn't infer that cases (b) and (c) are covered.

I'll update PR #298's description on the next revision to mark the scope boundary explicitly and cross-reference the case (b)/(c) gap as the unaddressable surface that depends on harness exposure of the missing fields. The carve-out keeps the hook honest about what it measures and what it doesn't, matching the §6.2 sub-table's honest-measurement-boundary discipline on the verification-axis side.

On Ask #6 — exposing programmatic credit consumed per-subagent. Accepting the proposal as a first-class measurement primitive worth filing alongside @meefs's Asks #1–#5. The reframing — turning the 2026-06-15 billing reclassification's side effect into an after-the-fact audit substrate for case (b) — is the cleanest path to case (b) measurability that doesn't require harness changes to the abort surface. Two operational properties make Ask #6 land cleanly:

  1. Audit-substrate vs. real-time-control separation. Ask #6 doesn't ask for a control primitive (which would require harness changes to the abort path); it asks for a measurement primitive that operators can read after the fact. That keeps the implementation cost low (a field exposure in the API response or a hook-readable artifact at SubagentStop) while delivering the case (b) measurement substrate operators need before they can argue for a downstream control.
  1. Pre-flight vs. post-hoc symmetry with the pre-flight runbook for #61704. The runbook I shipped yesterday for claude -p billing routing (Gist f936ba84) baselines parent-layer billing surfaces before automation; Ask #6's per-subagent credit field baselines child-layer attribution after the fact. The two together would give operators a closed loop on programmatic spend attribution: parent-layer baseline (pre-flight) + per-subagent attribution (post-hoc) = full causal chain on where the credit went. Without Ask #6, the per-subagent attribution gap means operators auditing post-2026-06-15 see only aggregate burn, not which subagent class drove it.

Worth filing as a standalone issue tagged for the 2026-06-15 reclassification window, framed as a billing-side-effect-as-measurement-primitive ask rather than as a control ask — that framing preempts the "we'd need to redesign the abort path" objection that would land on a control-primitive ask.

Forward. Three propagation items in the 72h window: (1) evidence_form column added to the Matrix Gist with positive/negative tagging across all current rows; (2) PR #298 description updated with the case (a) vs. (b)/(c) scope boundary; (3) Ask #6 filed as a standalone issue (I'll draft and post; flag if you want to co-file). The §6.2 sub-table propagation from the #61102 thread stays on the same 72h schedule. Next coordination point is when an N=9 row gets surfaced from a new lifecycle event that doesn't fit the existing rows, or when an operator-side empirical pass on cases (b)/(c) produces the first proxy measurement (the post-2026-06-15 credit-differential data from Ask #6, if filed and shipped in time).

— yurukusa

yurukusa · 3 months ago

Propagation status update (3/3 commitments fulfilled, within the 72h window).

  • Matrix Gist evidence_form column added (commit history). Rows 1–8 tagged positive (verification-axis). Row 9 added with negative (control-propagation, case (a) only) pointing at PR #298 as the dispatch-liveness-watchdog surface. The inverse-evidence-form asymmetry articulated in the surrounding prose explicitly carries: positive rows need a detection grammar against recorded receipts; negative rows need a constraint-propagation primitive plus a downstream measurement proxy. Audit-step §4 in the operator-guidance section now tells operators not to compose positive and negative rows into a single F1 number because they measure structurally different things.
  • PR #298 description scope-boundary block added (PR view). The cases (a)/(b)/(c) decomposition lands as a table at the top of the description. Operators reading the PR before installing the hook see explicitly that PR #298 covers case (a) only; cases (b) and (c) are flagged as the unaddressable control-propagation surface that depends on harness changes. The block also cross-references the Ask #6 candidate as the path to case (b) measurability.
  • Ask #6 filed as standalone issue: #61934 — "Expose programmatic credit consumed per-subagent in API response or SubagentStop hook input." Framed as a measurement primitive, not a control primitive — the field exposure on the existing dispatch-completion code path that reads a value billing already computes, no abort path or policy propagation. The 2026-06-15 forcing function is articulated: post-reclassification operators see aggregate billing burn whether or not they instrumented anything, but cannot attribute it without per-subagent exposure. Two equivalent implementation options (Option A: API response field; Option B: hook-readable artifact at SubagentStop) — either is sufficient.

Next coordination point per the previous lock is either an N=10 row from a new lifecycle event that doesn't fit the existing matrix, or an operator-side empirical pass on cases (b)/(c) once Ask #6 ships and the post-2026-06-15 credit-differential data becomes available.

— yurukusa

waitdeadai · 3 months ago

@yurukusa — confirmed, all three landed cleanly. #61934 captures the per-subagent credit ask as a measurement primitive exactly as scoped, and the positive/negative evidence_form split with the no-compose-into-one-F1 caveat is the right guardrail against collapsing distinct failure shapes into a single misleading number. No further coordination needed from my side until an N=10 row materializes or the post-2026-06-15 credit-differential pass surfaces a divergence worth tracking.

kcarriedo · 2 months ago

The timeout gap is the most dangerous one. An unbounded subagent delegation turns a bug in the delegated task into an hours-long runaway — and since there's no monitoring surface, the user can't tell whether the agent is making progress or spinning. The 12+ hour hang you describe is exactly what happens when the task decomposes to something that requires human input but the subagent has no ceiling.

From running Claude Code dispatches in an out-of-process coordinator (cron-driven Rust service with explicit per-step timeouts), a few concrete things that would materially reduce this failure class:

Subagent timeout (most impactful): A timeout_seconds param on Agent() / Dispatch() — if the child doesn't return within N seconds, the parent gets a SubagentTimeout result with whatever partial output accumulated. The parent can then decide: retry with a tighter prompt, escalate to human, or continue without that result. Right now the only option is to kill the entire session.

Progress heartbeat: every M seconds, the subagent writes a brief status line to a shared location — not a full state dump, just a "still alive, currently on step X" marker. The parent can check this to distinguish "slow" from "stuck." This is easy to implement as a hook convention even before there's native support.

Graceful delegation abort: /abort-task <subagent-id> in the TUI, or an API call that signals the child to wrap up and return whatever it has. SIGTERM semantics — not a kill.

For anyone hitting this now: the practical mitigation is to cap subagent task scope aggressively. Single-function, bounded-input tasks (not "review the whole workflow" but "check line 42 of deploy.yml for this specific condition") are far less likely to hang. Small tasks with a single exit condition rather than open-ended analysis.

This relates directly to #48965 (session registry so the parent can find and signal children) and #62631 (child turn-end not visible to parent). All three are symptoms of the same missing piece: observable, bounded subagent execution.

github-actions[bot] · 1 month ago

Closing for now — inactive for too long. Please open a new issue if this is still relevant.