#8523·multica

[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

Author: karlarsoCreated Sep 17, 2026Updated Sep 17, 2026

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 exceeded

caused by an ever-growing request body (base64 screenshots accumulated over a long tool loop).

Environment

  • multica CLI/daemon: 0.4.40 (linux, fedora), local runtime with pi
  • 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_watchdog

Note tool_in_flight=false: the agent was not stuck inside a tool call. The last thing the pi session recorded was the provider error:

json
{"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)

  1. 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.
  2. 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).
  3. 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.
  4. pi's overflow recovery only fires for recognized overflow patterns; this 413 does not match, so no "compact and retry once".
  5. 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_error plus the error string would have made this diagnosable in minutes instead of hours).

Suggested fixes (any of these)

  1. daemon: treat "agent finished with stopReason=error and no further output" as an immediate failure instead of waiting out the idle watchdog.
  2. pi: allow mapping provider-specific body-limit errors (HTTP 413 / length limit exceeded) to context_length_exceeded so auto-compaction + retry kicks in.
  3. 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:

bash
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.