[GH-ISSUE #162] bug: repeated context compaction can drop every user message and break Qwen/vLLM #82

Open
opened 2026-10-18 14:02:45 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @calderanicola on GitHub (Jul 23, 2026).
Original GitHub issue: https://github.com/open-webui/computer/issues/162

Summary

On cptr 0.9.12, a long agentic Chat Completions task can fail after repeated automatic context compaction when the retained keep_zone contains only assistant and tool messages.

With a Qwen model served by vLLM through LiteLLM, the next request fails with:

litellm.BadRequestError: Hosted_vllmException -
{"error":{"message":"No user query found in messages.","type":"BadRequestError","code":400}}

Raising chat.compact_token_threshold only delays the failure.

Environment

  • Computer/cptr: 0.9.12
  • Connection: OpenAI-compatible Chat Completions
  • Gateway: LiteLLM hosted_vllm
  • Backend: vLLM
  • Model group: Qwen
  • OS: Windows (but the compaction logic is platform-independent)

Reproduction

  1. Start a task from one normal user request.
  2. Let the agent execute a long sequence of tool calls and tool results.
  3. Allow automatic compaction to run more than once.
  4. On a later compaction, the only retained user message can fall into the new drop zone.
  5. The next upstream request contains a system prompt/summary followed only by assistant/tool turns.

A sanitized real trace reproduced the second compaction as:

messages before split: 263
split index:           158
messages kept:         105
user messages kept:    0

Root cause

In cptr/utils/chat_task.py:

  • _find_safe_split() protects tool-call/tool-result boundaries and keeps at least two messages.
  • It does not guarantee that keep_zone contains a top-level user turn.
  • After compaction, messages = keep_zone.
  • api_messages is then sent to the provider unchanged with respect to roles.

For a task that began with one user prompt followed by many assistant/tool iterations, repeated 40% retention can eventually remove every user turn.

Expected behavior

Every compacted continuation sent through Chat Completions should contain a real top-level user anchor, while preserving the retained assistant/tool ordering.

Suggested fix

Add a small invariant immediately before constructing ChatCompletionForm:

def _ensure_user_query(messages: list[dict]) -> list[dict]:
    if any(message.get("role") == "user" for message in messages):
        return messages
    return [
        {
            "role": "user",
            "content": (
                "Continue the task using the conversation summary "
                "and the tool results below."
            ),
        },
        *messages,
    ]

# After removing internal message IDs:
api_messages = _ensure_user_query(api_messages)

This keeps normal histories unchanged and adds one synthetic continuation turn only when compaction removed every user message.

Regression tests

  1. A history that already has a user message is returned unchanged.
  2. An assistant/tool-only history gets one non-empty user message prepended.
  3. Existing assistant/tool messages retain their exact order and content.
  4. Repeated compaction of a single-user agentic task still produces a provider request containing a user turn.
Originally created by @calderanicola on GitHub (Jul 23, 2026). Original GitHub issue: https://github.com/open-webui/computer/issues/162 ## Summary On cptr 0.9.12, a long agentic Chat Completions task can fail after repeated automatic context compaction when the retained `keep_zone` contains only `assistant` and `tool` messages. With a Qwen model served by vLLM through LiteLLM, the next request fails with: ``` litellm.BadRequestError: Hosted_vllmException - {"error":{"message":"No user query found in messages.","type":"BadRequestError","code":400}} ``` Raising `chat.compact_token_threshold` only delays the failure. ## Environment - Computer/cptr: 0.9.12 - Connection: OpenAI-compatible Chat Completions - Gateway: LiteLLM `hosted_vllm` - Backend: vLLM - Model group: Qwen - OS: Windows (but the compaction logic is platform-independent) ## Reproduction 1. Start a task from one normal user request. 2. Let the agent execute a long sequence of tool calls and tool results. 3. Allow automatic compaction to run more than once. 4. On a later compaction, the only retained user message can fall into the new drop zone. 5. The next upstream request contains a system prompt/summary followed only by assistant/tool turns. A sanitized real trace reproduced the second compaction as: ``` messages before split: 263 split index: 158 messages kept: 105 user messages kept: 0 ``` ## Root cause In `cptr/utils/chat_task.py`: - `_find_safe_split()` protects tool-call/tool-result boundaries and keeps at least two messages. - It does not guarantee that `keep_zone` contains a top-level `user` turn. - After compaction, `messages = keep_zone`. - `api_messages` is then sent to the provider unchanged with respect to roles. For a task that began with one user prompt followed by many assistant/tool iterations, repeated 40% retention can eventually remove every user turn. ## Expected behavior Every compacted continuation sent through Chat Completions should contain a real top-level user anchor, while preserving the retained assistant/tool ordering. ## Suggested fix Add a small invariant immediately before constructing `ChatCompletionForm`: ```python def _ensure_user_query(messages: list[dict]) -> list[dict]: if any(message.get("role") == "user" for message in messages): return messages return [ { "role": "user", "content": ( "Continue the task using the conversation summary " "and the tool results below." ), }, *messages, ] # After removing internal message IDs: api_messages = _ensure_user_query(api_messages) ``` This keeps normal histories unchanged and adds one synthetic continuation turn only when compaction removed every user message. ## Regression tests 1. A history that already has a user message is returned unchanged. 2. An assistant/tool-only history gets one non-empty user message prepended. 3. Existing assistant/tool messages retain their exact order and content. 4. Repeated compaction of a single-user agentic task still produces a provider request containing a user turn.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
open-webui/computer#82
No description provided.