Skip to content

0.49.22 (omp 18.13.0): after an aborted turn, every Anthropic turn is refused with prepared turn structure … expectedRows > actualRows comparison=role (empty-text aborted assistant dropped by omp, kept by the fence) #794

Closed

Description

@TheOrdinaryWow

Summary

On the Anthropic Messages lane, once a turn has been refused and aborted, every later turn in that session is refused:

stage=provider dispatch … refused (provider request does not match this pass's managed dispatch (prepared turn structure);
row=3 expectedRows=7 actualRows=5 expectedRole=assistant actualRole=user
expectedCalls=0 actualCalls=0 expectedResults=0 actualResults=0 comparison=role)

Retrying, or sending the session a new message, never helps. The wire always has fewer rows than the prepared structure, and the gap equals the number of aborted assistant turns whose content is a single empty text block.

Environment

What happens

  1. A turn is refused (in my case by bug(omp): first turn after a restart is refused — a cancelled tag-snapshot prewarm is treated as a hydration failure (0.49.22) #790's Pi tag hydration: cancelled). Magic Context calls ctx.abort(), and the host persists an assistant message with stopReason: "aborted" and content: [{ "type": "text", "text": "" }].
  2. On the next pass, the prepared structure keeps that assistant row.
  3. omp's Anthropic converter (@oh-my-pi/pi-ai providers/anthropic.ts, assistant branch of the message conversion) skips text blocks where text.trim().length === 0, and then skips the whole message when blocks.length === 0. The aborted turn is gone from the wire, so the next row is the following user/developer message: expectedRole=assistant actualRole=user.
  4. That refusal aborts again. This time the persisted message has content: [], which both sides drop (it matches shouldDropTruncatedThinkingOnlyAssistant), so the gap stays constant and every pass refuses.

Two sessions, each pass in order (the delta stays at the number of ""-text aborted turns):

session aborted turns with [{text:""}] refusals (expected / actual rows)
1 2 7/5, 8/6, 9/7
2 1 5/4, 6/5, 7/6, 8/7

The same abort on the OpenAI Responses lane (same [{text:""}] content) is not affected, and those sessions recovered on the next served pass.

Suggested direction

Related: #789 (Anthropic Continue. repair row, where the wire has more rows) and #790 (the refusal that creates the first aborted turn here).

Activity

  1. magic-alfonso commented on Oct 26, 2026

    @magic-alfonso

    Thanks for the precise report. The cause is as you describe. Oh My Pi's Anthropic converter skips text blocks that are empty or whitespace-only, and it then drops an assistant message that has no blocks left. The aborted turn's [{type: "text", text: ""}] therefore never reaches the wire. Magic Context's request check still counts that row, so the row count stays off by one for each such turn, and every later pass is refused. The OpenAI Responses route sends the empty message, which is why it recovers there.

    We're preparing a fix that models this drop in the request check, using the same converter rules for each supported Oh My Pi version. Until it ships, starting a new session avoids the loop.

  2. magic-alfonso commented on Oct 27, 2026

    @magic-alfonso

    Fixed in 0.49.24. Thanks @TheOrdinaryWow for the report and the trace.

    After an aborted turn, Oh My Pi drops an assistant reply that has no blocks left, and Magic Context's request check now models that drop, using rules read from Oh My Pi's source for each supported version (18.12.3, 18.12.6 and 18.13.0). Later Claude turns are no longer refused. On a real Oh My Pi 18.13.0 session, the previous build refused every turn after one aborted turn and this build replies.

    One narrow case still refuses rather than guesses: a model configured to replay thinking from another provider, whose thinking is then dropped at request time. If you hit that, please open an issue with the log line.

    Update the Pi package and restart Oh My Pi to pick it up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    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