[Bug]: run stays silent for the full 2h idle watchdog after a terminal provider error (HTTP 413 request-body limit) — no failure reported until force-stop
Summary
When the model provider returns a terminal error, the pi agent process stops producing messages but the run is not reported as failed. The daemon's idle watchdog cannot distinguish "agent failed" from "agent is thinking", so the task sits idle for the whole inactivity budget (2h) and is only then force-stopped. Nothing is surfaced during that window: no failure, no comment, no alert — output_bytes=0, and the issue stays open with no trace.
In our case the trigger was an HTTP 413 from the provider gateway:
413 Failed to buffer the request body: length limit exceededcaused by an ever-growing request body (base64 screenshots accumulated over a long tool loop).
Environment
multicaCLI/daemon:0.4.40(linux, fedora), local runtime withpi- provider/model:
deepseek/deepseek-flash(OpenAI-compatible,https://api.deepseek.com) - agent task: issue-scoped run with a long tool loop (
adb+ mobile-mcp, one screenshot per UI step) — this is the screenshot-heavy case, but the failure mode is provider-agnostic
Actual behaviour
Daemon log of one such run (local time), shortened:
00:15:46.434 INF tool #135: read
00:15:47.943 INF tool_result observed ... seq=536 tool=read
00:15:48.357 DBG reported task messages ... count=1 last_seq=536
<-- nothing at all for 2h04m -->
02:20:01.684 WRN idle watchdog firing: no agent activity, force-stopping run
idle_for=2h4m14s threshold=2h0m0s tool_in_flight=false
02:20:01.684 INF agent finished status=idle_watchdog duration=2h15m2s tools=135
02:20:01.684 DBG agent result detail ... output_bytes=0 session_id="" models_with_usage=0
02:20:02.031 INF task did not complete, reporting failure status=blocked failure_reason=idle_watchdogNote tool_in_flight=false: the agent was not stuck inside a tool call. The last thing the pi session recorded was the provider error:
{"type":"message","message":{"role":"assistant","content":[],"stopReason":"error",
"errorMessage":"413 Failed to buffer the request body: length limit exceeded"}}After that entry the session file was not written for 2 hours.
Root cause (analysis)
- OpenAI-compatible chat requests are stateless: every request re-uploads the whole history, including every inlined image. 39–41 full-resolution screenshots (1080×2376 PNG, ~1.3–2.1 MB each) accumulated to ~50 MB of base64 in one session; the gateway rejects request bodies above ~50 MB.
images.autoResize(cap 2000×2000) barely helps a tall phone screenshot: the long edge is only trimmed 2376→2000 (measured 2.24 MB → 2.10 MB).- pi's auto-compaction is token-triggered (
contextTokens > contextWindow - reserveTokens≈ 983k tokens for a 1M window); the runs were only at 100–290k tokens, so compaction never ran. The limit that killed the run is a byte limit, not a token limit. - pi's overflow recovery only fires for recognized overflow patterns; this 413 does not match, so no "compact and retry once".
- The daemon never learns any of this: the run just goes quiet until the watchdog fires.
Measured wall (2 independent runs):
| run | last successful request | + next screenshot | result |
|---|---|---|---|
| 1 | 48.40 MB | +2.79 MB = 51.19 MB | 413 |
| 2 | 49.96 MB | +0.82 MB = 50.78 MB | 413 |
i.e. ~38–41 full-resolution screenshots is the practical per-session ceiling.
Expected behaviour
- Before declaring a run idle, check whether the agent ended its turn with a terminal error and fail immediately (keep the long inactivity budget only for genuinely silent hangs).
- Surface the provider error to the task record / issue (even a
failure_reason=provider_errorplus the error string would have made this diagnosable in minutes instead of hours).
Suggested fixes (any of these)
- daemon: treat "agent finished with
stopReason=errorand no further output" as an immediate failure instead of waiting out the idle watchdog. - pi: allow mapping provider-specific body-limit errors (HTTP 413 /
length limit exceeded) tocontext_length_exceededso auto-compaction + retry kicks in. - docs: document the practical per-session image budget for screenshot-heavy runtimes, and recommend downscaling before images enter context.
Workaround we applied
Downscale screenshots before they enter the context instead of feeding full-resolution PNGs:
adb exec-out screencap -p | magick - -resize 540x -quality 65 jpg:- > shot.jpg
# measured: 1,771,699 B -> 54,038 B (33x smaller)plus a per-run check of inline-image bytes against the ~50 MB ceiling (reads the pi session file, sums inlined image base64, exits non-zero when the remaining budget is low). With 540px JPEGs the ceiling moves from ~38 screenshots to ~900 per session.
Source: multica-ai/multica