Replies: 1 comment 1 reply
|
Verified the full chain against v0.1.1-rc.2 source (your session-seq evidence is excellent — it made the mechanism unambiguous). The bug is real; I would decompose it into three independent layers, because each has a different fix and the deepest one is a systemic pattern. L1 — trigger: DSH re-encodes to WebP without checking provider support L2 — escalation: a provider request error becomes a hard turn abort, not a failed tool result L3 — replay: "continue" rebuilds the identical poisoned context, so the failure is sticky forever Family: this is the third member of the sticky turn-end family — #3685 (sticky max-tokens turn-end, agent.ts:285-290/:410), #2821 (unbounded step loop with no max-steps), and now #4048 (sticky error replay). Common gap: a turn-end reason that repeats identically on resume with no context repair. My "snapshot vs live" analysis (#3552/#3565) has the same lesson: when a persisted artifact becomes poisonous, inject a live/fixed source instead of replaying the snapshot. Fix directions (in increasing depth)
Immediate workaround: re-encode the image to PNG before read_image (or drop the attachment), so the WebP path never triggers — a new session on the same conversation avoids the replay only if the image is gone from the input. If you can share whether the 400 arrives at request start (before any chunk, as your inputTokens=0 suggests) vs mid-stream, it would confirm whether L2's fix belongs in the stream-rejection path ( |
Uh oh!
There was an error while loading. Please reload this page.
Bug: failed tool image result hard-aborts the turn and is replayed on every "continue", so the session can never recover
Summary
When a
read_imagetool result carries an image that the LLM provider rejects(in this case LM Studio returns
400 "'url' field must be a base64 encoded image."for a WebP data URI), DSH:
turn/end reason: "error") instead of returning it to the model as a failed tool result(
is_error: true), so the model never gets a chance to recover (e.g. re-encodeto PNG and retry).
including the rejected image still sitting in the tool-result context — so the
very first model call of the resumed turn fails with the identical error
again. DSH does not even issue a fresh LLM request; it errors before producing
any tokens. This repeats forever; the conversation is effectively dead.
Environment
ornith1.5-9b(a VLM).read_imageon an image DSH re-encodes to WebP (e.g. a PNG carrying atext comment chunk). LM Studio's OpenAI-compat endpoint rejects
image/webp.Reproduced
Session
session-c62bdc04-464e-46f2-967e-f78d47076aa4(user title"上个会话本来是要画个……").
Turn 2 — the failure (session event
seqs)The provider rejected the image the moment the model's next step tried to send it.
Instead of feeding this back as a failed
tool_result, DSH aborts the whole turn.Turns 3/4/5 — "继续" replays the dead state (each ~87 minutes apart)
User messages between turns:
继续,去读_im2.png(turn 3),继续(turn 4),继续(turn 5). Note that none of turns 3-5 produced arequest/headerevent:DSH never makes a fresh model call. It reconstructs the conversation (which still
contains the rejected WebP image in the tool-result context), attempts the first
model step, and fails with the exact same provider error before generating any
tokens.
Root cause
Two coupled problems:
tool result. The model never sees "read_image/that image failed with
", so it cannot adapt (e.g. strip metadata, re-save as PNG, or skip the
image and proceed).
rejected image stays in context, so every resumed first step reproduces the
exact same provider 400. Because the error is a turn-level abort (not a
recorded tool error), there is no "recover from the failure" path — the state
is re-sent as-is and dies at the same point.
Expected behaviour
model as a
tool_resultwithis_error: truecontaining the error message,so the model can decide how to recover. Only genuinely unrecoverable conditions
should abort the turn.
re-send the failed tool output that the provider already rejected. It should
either drop/neutralize the failed tool result, surface the previous error to the
model, or otherwise allow the resumed turn to make a fresh decision instead of
looping on the identical provider rejection.
Impact
schema) turns into a permanent session hang: the model can never recover, and
"继续" is useless because the failing input is replayed forever.
session-death behaviour above is provider-agnostic and is the real bug worth
fixing independently of the WebP issue.
Suggested area
results instead of re-sending them).
All reactions