Skip to content

buzz-dev-mcp: the 600s shell cap is silently clamped and unconfigurable — long agent jobs die mid-work with no signal #4638

Description

@jdrolls

Summary

crates/buzz-dev-mcp/src/shell.rs caps every shell command at 600s and there is no way to raise it, no env var, and — the part that costs the most debugging time — no report that the clamp happened. A caller that asks for 3600s is silently given 600s, and on expiry the whole process group is killed.

Where

// crates/buzz-dev-mcp/src/shell.rs
const DEFAULT_TIMEOUT_MS: u64 = 120_000;   // :16
const MAX_TIMEOUT_MS: u64 = 600_000;       // :17

let timeout_ms = p
    .timeout_ms
    .unwrap_or(DEFAULT_TIMEOUT_MS)
    .min(MAX_TIMEOUT_MS);                  // :141-144

cmd.kill_on_drop(true);                    // :178
set_process_group(&mut cmd);               // :179

.min(MAX_TIMEOUT_MS) is the whole story: timeout_ms is an accepted parameter of the tool (:127 pub timeout_ms: Option<u64>), so a caller reasonably believes it was honoured. It was not. Verified present at HEAD, not just on the v0.5.3 tag.

Impact

Any agent-launched job longer than 10 minutes dies mid-work. Because set_process_group + kill_on_drop take out the entire group, the child's own output buffer goes with it, so what the agent sees is a truncated or empty log — not a timeout message.

Concretely, on a self-hosted relay driving an agent that fixes issues in a repo: four consecutive runs died at exactly 600s. Each had already done real work (written files, opened PRs, posted review comments) before the kill. The surviving artifact was a 15-byte log reading Execution error. The agent, given only that, concluded three separate times that its model backend was broken and proposed switching runners — a diagnosis that would have made things worse, since the same ceiling kills any runner identically. The ceiling is invisible from inside the sandbox; nothing in the failure points at it.

Asks (either would be sufficient, in preference order)

  1. Make the cap configurable — an env var (BUZZ_DEV_MCP_MAX_TIMEOUT_MS) or a config field. Operators running agents on their own hardware are already trusting them with a shell; a hard 10-minute ceiling is a policy decision that should belong to the deployment.
  2. At minimum, report the clamp. If p.timeout_ms > MAX_TIMEOUT_MS, say so in the tool result, and on expiry return an explicit "killed at N s ceiling" rather than letting the caller infer it from a truncated stream. A silent clamp that a caller cannot detect is indistinguishable from a crash.

Not covered by existing work

PR #935 ("feat(acp): agent timeout resilience") is adjacent and explicitly acknowledges the "600s max shell timeout" — it raises DEFAULT_IDLE_TIMEOUT_SECS 620 → 900 so the ACP idle timer stops firing during legitimate long tool calls. That fixes spurious session kills above the cap; it leaves the cap itself, and its silence, unchanged.

Workaround for anyone hitting this

Launch through a setsid wrapper so the run lives in a session the group-kill cannot reach, and have the wrapper — not the agent — own the launch, since the detached run then outlives the MCP call that started it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions