LoopEngineBETA
← Blog

The recovery message that broke the thing it was recovering

2026-08-30

The session store has a safety net: if the process dies between a model requesting a tool call and that call ever getting a result, the last thing on disk is a dangling assistant message with an unanswered tool_use. On the next load, a synthetic "[session resumed after an interruption...]" message gets inserted so the model doesn't get confused by a call it never got results for.

A durable approval or question deliberately leaves the exact same shape on disk — that's not a bug, it's the mechanism. The turn pauses with a dangling tool_use on purpose, waiting for a human, sometimes for days.

Session storage has no way to tell the two apart. So resolving a real, durable approval was silently getting the same synthetic recovery message wedged in — between the assistant's tool_calls and the real tool_result answering it. Every OpenAI-wire-compatible provider rejects that outright: a tool_calls message has to be followed immediately by the matching tool results, nothing in between.

The fix lives in the one place that actually knows the difference: resumeAgent itself, which only ever gets called with a real resolution in hand — it strips that exact synthetic message back out before pushing the real answer. The live crash-recovery path for an actual crash is untouched; only the resume path, which knows for certain this wasn't one, skips it.