Skip to content

Anthropic refusals are saved as successful empty replies: stop reason is never captured, turn looks blank and done, nothing in the logs #316

Open

Description

@Classic298

What happens

Claude turns come back completely empty. The reply bubble is blank, the turn is marked as finished, and there is no error anywhere: not in the chat, not on the message, not in the logs. The saved reply is a done message with empty content and zero output tokens, and the log shows a normal successful request. To the user it looks like Claude randomly stopped answering: some turns reply, some turns save a blank bubble and the conversation just continues.

Confirmed on 0.9.21 against the live chat database and logs, with the API side reproduced by replaying the exact same request outside cptr.

Why

When Anthropic's safety layer refuses a request, the API does not send an error. It sends HTTP 200 and a normal-looking stream that ends immediately with zero content and the stop reason refusal. The stop reason arrives in the final stream event:

{"type":"message_delta","delta":{"stop_reason":"refusal"},"usage":{"output_tokens":0}}

cptr never reads it. The stream handler only takes the usage numbers from that event and throws the rest away, so a refusal is indistinguishable from a completion that happened to say nothing, and the turn is saved as a success. Two things are missing: the stop reason is never captured anywhere, and a finished turn with zero output raises no warning at all.

Repro

  1. Use the Anthropic connection with tools enabled, on a workspace whose file listing (the tree cptr puts in the system prompt) contains security-research-looking names, e.g. decrypt_key.py, poc_part1.py, repro_race.py. I hit this with real leftover debugging scratch sitting in the workspace.
  2. Send short messages like TEST! in a chat with a Claude model.
  3. Roughly 9 out of 10 turns come back as empty replies saved as done. The occasional turn goes through normally, which makes the whole thing look intermittent.

The same request replayed outside cptr shows the full picture: message_start, the final event with stop_reason: "refusal" and zero output tokens, message_stop. No content blocks at all. cptr logs status=200 and a successful save for the same request.

Expected versus actual

Expected: a refused turn is an error state in the chat with the stop reason visible in the message, and at minimum a log line carrying the stop reason.
Actual: a refused turn is saved as a successful empty reply, done flag set, no error on the message, nothing in the logs.

Consequences

  • In an affected workspace the Anthropic connection looks totally broken: nearly every turn is a blank reply while the logs say everything succeeded. There is no way to tell a refusal from a network problem from a bug.
  • The empty turns pile up in the conversation history with no explanation.
  • Every other stop reason is swallowed the same way; refusal is just the one that produces total silence.

Fix direction

Read the stop reason from the final stream event and act on it: treat refusal as a failed turn, store it as the message error with the stop reason in the text, and log it. Independently of that, a turn that finishes with no text and no tool calls should not be saved as a quiet success; flag it in the log so silent empty replies are always diagnosable.

Related: #293, same class of lost diagnostic information, there for provider error payloads, here for the Anthropic stop reason.

Workaround for now

None inside cptr. To find out whether empty replies are refusals, replay the request outside cptr and read the stop reason from the stream. The refusals themselves are triggered by prompt content, in my case the workspace file listing; renaming those files stopped the refusals. That part is content on the user side, but cptr should still say what happened instead of presenting a blank successful reply.

Activity

  1. owui-terminator commented on Oct 12, 2026

    @owui-terminator

    ⚠️ Missing Issue Title Prefix

    @Classic298, your issue title is missing a categorising prefix (e.g. bug:, feat:, docs:).

    Please update the title to lead with one of:

    • bug: bug report or error
    • feat: feature request or enhancement
    • docs: documentation issue
    • question: usage question
    • help: support request

    Example: bug: Login fails when the password contains special characters

  2. owui-terminator commented on Oct 12, 2026

    @owui-terminator

    🔍 Related Issues Found

    I found some existing issues that might be related. Please check if any of these are duplicates or contain helpful solutions:

    1. 🟢 #209 bug: task silently marked complete when the post-compaction completion returns zero tokens
      Both issues describe a completion request that returns HTTP 200 with zero output and is then saved as a normal finished turn. Here the missing signal is zero-token completion after compaction; in Anthropic refusals are saved as successful empty replies: stop reason is never captured, turn looks blank and done, nothing in the logs #316 it is Anthropic's final-stream stop_reason: refusal, but the core bug is the same: a silent successful empty reply instead of an error/diagnostic state.
      by ThalynLabs

    2. 🟢 #293 bug: user-facing error discards the provider's reason, only shows "Provider returned error"
      bug: user-facing error discards the provider's reason, only shows "Provider returned error" #293 is about losing the provider's real failure reason and showing only a generic success/failure wrapper. Anthropic refusals are saved as successful empty replies: stop reason is never captured, turn looks blank and done, nothing in the logs #316 is the same class of bug for Anthropic streaming: the final event's stop_reason is discarded, so the UI/logs lose the actual reason the turn produced no assistant content.
      by Classic298


    💡 If your issue is a duplicate, please close it and add any additional details to the existing issue instead.

    This comment was generated automatically. React with 👍 if helpful, 👎 if not.

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