Skip to content

muse-proxy: a run that fails after producing partial output is reported as a successful 'stop' — failure is masked whenever any content exists #672

Closed

Description

@dezren39

Problem

bin/muse-proxy.mjs decides whether a muse exec run failed with this predicate (line 439):

const failed = (code !== 0 && fullContent.length === 0) || (terminalState && terminalState !== 'completed' && fullContent.length === 0);

Both clauses require fullContent.length === 0. So whenever the CLI emitted any assistant text before failing, the failure is masked: the proxy returns the partial text as a normal successful completion with finish_reason: 'stop'.

Evidence (verified 2026-10-23 with a stub muse on PATH, same harness style as bin/muse-proxy.test.mjs)

Case A — terminal event says the run failed, exit code 0:

  • stub emits run.output.delta "partial answer", then run.terminal.failed with terminal: "failed", exit 0.
  • Non-stream response: 200, message.content: "partial answer", finish_reason: "stop".
  • Stream response: partial chunk, then a finish chunk with finish_reason: "stop" + [DONE]. No error anywhere.

Case B — child crashes mid-run, exit code 3:

  • stub emits run.output.delta "partial answer", writes stderr, exit 3 (no terminal event).
  • Both modes: 200 / finish_reason "stop" with the partial text, exactly as Case A.

The empty-content cases are handled correctly (#670: non-stream 500, stream SSE error frame + [DONE]); only the partial-content cases slip through.

Why it matters

Clients (opencode and any OpenAI-compatible consumer) cannot distinguish a truncated, failed run from a complete answer: they present the partial text as the model's full reply and record the request as successful. A mid-run auth expiry, rate limit, or CLI crash after the first tokens is indistinguishable from success — the worst failure shape for a bridge whose whole job is faithful translation.

Acceptance criteria

Suggested fix

Make the terminal state authoritative and drop the content-length gate, e.g.:

const failed = terminalState ? terminalState !== 'completed' : code !== 0;

Related sighting (judge separately)

bin/agy-proxy.mjs:553 has a similar content-gated check (code !== 0 && !responseText). That CLI exposes no authoritative terminal event, so the semantics differ; noted here only so the fixer can decide whether it deserves its own issue — it is not part of this issue's acceptance criteria.

Activity

  1. added a commit that references this issue on Oct 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

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