openclaw - 💡(How to fix) Fix [Bug] Anthropic Claude Opus 4.7 + thinking returns 400 invalid_request_error due to assistant message prefill [2 comments, 3 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#72739Fetched 2026-04-28 06:32:46
View on GitHub
Comments
2
Participants
3
Timeline
3
Reactions
1
Timeline (top)
commented ×2closed ×1

When using anthropic/claude-opus-4-7 (or any Opus 4.7+ / Sonnet 4.6 / Mythos Preview) with thinking enabled, every API request fails with HTTP 400 because OpenClaw's default request payload appends an assistant-role message prefill ({) to guide JSON output. Anthropic explicitly removed prefill support in these models as a breaking change (official docs).

This causes:

  1. Every Telegram/main session request to fail
  2. Failover decisions to mis-classify it as format error → trigger compaction → retry → infinite loop
  3. Event loop saturation → server health-check timeout

Error Message

  1. Failover decisions to mis-classify it as format error → trigger compaction → retry → infinite loop error=LLM request rejected: This model does not support assistant message prefill. rawError=400 {"type":"error","error":{"type":"invalid_request_error", log.warn("removing assistant prefill because thinking is enabled (Anthropic restriction)");

Root Cause

In dist/extensions/anthropic/stream-wrappers.js, wrapAnthropicProviderStream does not check if the trailing message is role: "assistant" (prefill) before sending to Anthropic API. Anthropic's Opus 4.7 / Sonnet 4.6 / Mythos Preview reject any conversation that doesn't end with a user message.

Reference:

Fix Action

Fix / Workaround

Workaround (drop-in patch)

function createAnthropicPrefillFixWrapper(baseStreamFn) {
  const underlying = baseStreamFn ?? streamSimple;
  return (model, context, options) => {
    return streamWithPayloadPatch(underlying, model, context, options, (payloadObj) => {
      if (Array.isArray(payloadObj.messages) && payloadObj.messages.length > 0) {
        const last = payloadObj.messages[payloadObj.messages.length - 1];
        if (last && last.role === "assistant" && payloadObj.thinking) {
          log.warn("removing assistant prefill because thinking is enabled (Anthropic restriction)");
          payloadObj.messages.pop();
        }
      }
    });
  };
}

### Verification (post-patch)

Code Example

{
  "agents": {
    "defaults": {
      "model": "anthropic/claude-opus-4-7",
      "thinking": "high"
    }
  }
}

---

[agent/embedded] embedded run agent end: isError=true
  model=claude-opus-4-7 provider=anthropic
  error=LLM request rejected: This model does not support assistant message prefill.
        The conversation must end with a user message.
  rawError=400 {"type":"error","error":{"type":"invalid_request_error",
                "message":"This model does not support assistant message prefill.
                           The conversation must end with a user message."}}

---

function createAnthropicPrefillFixWrapper(baseStreamFn) {
  const underlying = baseStreamFn ?? streamSimple;
  return (model, context, options) => {
    return streamWithPayloadPatch(underlying, model, context, options, (payloadObj) => {
      if (Array.isArray(payloadObj.messages) && payloadObj.messages.length > 0) {
        const last = payloadObj.messages[payloadObj.messages.length - 1];
        if (last && last.role === "assistant" && payloadObj.thinking) {
          log.warn("removing assistant prefill because thinking is enabled (Anthropic restriction)");
          payloadObj.messages.pop();
        }
      }
    });
  };
}

// Then in wrapAnthropicProviderStream, add as last wrapper:
return composeProviderStreamWrappers(
  ctx.streamFn,
  /* existing wrappers */,
  (streamFn) => createAnthropicPrefillFixWrapper(streamFn)
);
RAW_BUFFERClick to expand / collapse

Summary

When using anthropic/claude-opus-4-7 (or any Opus 4.7+ / Sonnet 4.6 / Mythos Preview) with thinking enabled, every API request fails with HTTP 400 because OpenClaw's default request payload appends an assistant-role message prefill ({) to guide JSON output. Anthropic explicitly removed prefill support in these models as a breaking change (official docs).

This causes:

  1. Every Telegram/main session request to fail
  2. Failover decisions to mis-classify it as format error → trigger compaction → retry → infinite loop
  3. Event loop saturation → server health-check timeout

Reproduction

Environment: OpenClaw 2026.4.25-beta.10, Node 24.14.1, Linux ARM64

Config:

{
  "agents": {
    "defaults": {
      "model": "anthropic/claude-opus-4-7",
      "thinking": "high"
    }
  }
}

Steps: Send any message via Telegram or any inbound channel.

Expected: Successful response with thinking-enhanced output.

Actual:

[agent/embedded] embedded run agent end: isError=true
  model=claude-opus-4-7 provider=anthropic
  error=LLM request rejected: This model does not support assistant message prefill.
        The conversation must end with a user message.
  rawError=400 {"type":"error","error":{"type":"invalid_request_error",
                "message":"This model does not support assistant message prefill.
                           The conversation must end with a user message."}}

Root Cause

In dist/extensions/anthropic/stream-wrappers.js, wrapAnthropicProviderStream does not check if the trailing message is role: "assistant" (prefill) before sending to Anthropic API. Anthropic's Opus 4.7 / Sonnet 4.6 / Mythos Preview reject any conversation that doesn't end with a user message.

Reference:

Workaround (drop-in patch)

Add a wrapper that strips the trailing assistant prefill when payloadObj.thinking is set:

function createAnthropicPrefillFixWrapper(baseStreamFn) {
  const underlying = baseStreamFn ?? streamSimple;
  return (model, context, options) => {
    return streamWithPayloadPatch(underlying, model, context, options, (payloadObj) => {
      if (Array.isArray(payloadObj.messages) && payloadObj.messages.length > 0) {
        const last = payloadObj.messages[payloadObj.messages.length - 1];
        if (last && last.role === "assistant" && payloadObj.thinking) {
          log.warn("removing assistant prefill because thinking is enabled (Anthropic restriction)");
          payloadObj.messages.pop();
        }
      }
    });
  };
}

// Then in wrapAnthropicProviderStream, add as last wrapper:
return composeProviderStreamWrappers(
  ctx.streamFn,
  /* existing wrappers */,
  (streamFn) => createAnthropicPrefillFixWrapper(streamFn)
);

Verification (post-patch)

Period400 prefill errorsSuccessful intercepts
Pre-patch (13:00–13:56)520
Post-patch (13:56–17:55)016

Suggested Fix (more robust than my workaround)

Recommend handling this at the model-capability layer:

  1. Add a supports_prefill capability flag to Anthropic provider model registry
  2. Default false for Opus 4.7+, Sonnet 4.6+, Mythos Preview
  3. When false and last message role is assistant AND thinking is enabled → strip OR convert to a soft constraint in system prompt
  4. Also consider adding output_config.format: "json" when supported (per Anthropic migration guide)

Related Issues

  • #67710 (Opus 4.7 model resolver)
  • #66462 (closed by #67024 — addresses compaction loop on similar pattern)
  • #68241 (claude-opus-4-7 rejected as Unknown model)

extent analysis

TL;DR

The most likely fix is to add a wrapper that strips the trailing assistant prefill when payloadObj.thinking is set for Anthropic models Opus 4.7+, Sonnet 4.6+, and Mythos Preview.

Guidance

  • Identify if the issue is caused by the thinking feature being enabled for Anthropic models that do not support prefilling assistant messages.
  • Apply the provided drop-in patch to strip the trailing assistant prefill when payloadObj.thinking is set.
  • Consider implementing a more robust fix by handling this at the model-capability layer, including adding a supports_prefill capability flag to the Anthropic provider model registry.
  • Verify the fix by monitoring the number of 400 prefill errors and successful intercepts.

Example

The provided drop-in patch code snippet can be used as a starting point:

function createAnthropicPrefillFixWrapper(baseStreamFn) {
  // ...
}

This code strips the trailing assistant prefill when payloadObj.thinking is set.

Notes

The provided workaround may not be the most robust solution, and handling this at the model-capability layer may be a more comprehensive fix. Additionally, this issue may be related to other issues, such as #67710, #66462, and #68241.

Recommendation

Apply the provided drop-in patch as a temporary workaround, and consider implementing a more robust fix by handling this at the model-capability layer. This will ensure that the issue is fully resolved and prevent similar issues from occurring in the future.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - 💡(How to fix) Fix [Bug] Anthropic Claude Opus 4.7 + thinking returns 400 invalid_request_error due to assistant message prefill [2 comments, 3 participants]