openclaw - 💡(How to fix) Fix [Bug]: History compaction strips all user/assistant turns on Qwen, leaving messages[] with only system → upstream 400

Official PRs (…)
ON THIS PAGE

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…

When OpenClaw's history compaction runs on long conversations with Qwen models (Qwen-Long, Qwen-3-Coder-Plus), the outbound messages[] sometimes ends up with no role: user or role: assistant entries — only role: system (or only role: tool) survives.

Qwen upstream rejects with HTTP 400:

  • Role must be user or assistant and Content length must be greater than 0
  • Role specification invalid

The user-facing symptom (in deployments that route through a proxy that wraps 4xx) is a generic stream-failed message that hides the real error. Without proxy wrapping the operator sees the 400 directly, but the request is always invalid.

Error Message

The user-facing symptom (in deployments that route through a proxy that wraps 4xx) is a generic stream-failed message that hides the real error. Without proxy wrapping the operator sees the 400 directly, but the request is always invalid.

  • (b) Fall back to a user-visible "history exhausted, please start a new session" error rather than letting upstream return an opaque 400

Root Cause

When OpenClaw's history compaction runs on long conversations with Qwen models (Qwen-Long, Qwen-3-Coder-Plus), the outbound messages[] sometimes ends up with no role: user or role: assistant entries — only role: system (or only role: tool) survives.

Qwen upstream rejects with HTTP 400:

  • Role must be user or assistant and Content length must be greater than 0
  • Role specification invalid

The user-facing symptom (in deployments that route through a proxy that wraps 4xx) is a generic stream-failed message that hides the real error. Without proxy wrapping the operator sees the 400 directly, but the request is always invalid.

Fix Action

Fix / Workaround

  • OpenClaw: 2026.4.2 (planning to upgrade to 2026.5.22)
  • Models affected: Qwen-Long (10M context), Qwen-3-Coder-Plus (1M context)
  • Provider type: openai-completions compatible
  • Trigger threshold: ~20K tokens of history before LCM compaction fires
RAW_BUFFERClick to expand / collapse

Summary

When OpenClaw's history compaction runs on long conversations with Qwen models (Qwen-Long, Qwen-3-Coder-Plus), the outbound messages[] sometimes ends up with no role: user or role: assistant entries — only role: system (or only role: tool) survives.

Qwen upstream rejects with HTTP 400:

  • Role must be user or assistant and Content length must be greater than 0
  • Role specification invalid

The user-facing symptom (in deployments that route through a proxy that wraps 4xx) is a generic stream-failed message that hides the real error. Without proxy wrapping the operator sees the 400 directly, but the request is always invalid.

Environment

  • OpenClaw: 2026.4.2 (planning to upgrade to 2026.5.22)
  • Models affected: Qwen-Long (10M context), Qwen-3-Coder-Plus (1M context)
  • Provider type: openai-completions compatible
  • Trigger threshold: ~20K tokens of history before LCM compaction fires

Repro pattern

  1. Start a session with Qwen-Long or Qwen-3-Coder-Plus
  2. Run 15-20+ turns with mixed tool use + direct chat to push past the softThreshold
  3. Wait for LCM compaction
  4. The next user turn → outbound request has messages: [ { role: 'system', ... } ] (and occasionally a trailing role: tool block), with every prior user/assistant turn gone
  5. Qwen 400 → request fails

Why this looks distinct from already-tracked compaction issues

Existing issueWhat it coversWhy this is different
#32759 (closed)"no conversation messages at all" guard, returns 400 vs graceful skipThis produces a message array that has some entries, but none of them user/assistant. The guard at #32759 doesn't fire.
#69756 (closed)"chat.history omitted: message too large" replacementThat replacement preserves placeholder text; our case strips entries entirely.
#74073 (open)auto-compaction race for readSubagentOutputRace-condition focus, single-call; our case is the steady-state result of compaction summarization.

Suggested fix direction

After compaction transforms messages[], the serializer should validate that at least one entry has role: 'user' or role: 'assistant' before transmitting. If not, either:

  • (a) Re-run compaction with a less-aggressive ratio, preserving the most recent user/assistant pair verbatim, or
  • (b) Fall back to a user-visible "history exhausted, please start a new session" error rather than letting upstream return an opaque 400

Option (a) is closer to graceful UX, option (b) is a clearer signal to the operator.

Offered

Happy to provide sanitized session jsonls + outbound request bodies if useful for a regression test. We hit this enough times in production that we can produce captures on demand.

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