Skip to content

bug: 500 from strict Jinja chat templates when the message history has no user-role message #307

Description

@Glitch303

Summary
When cptr sends an OpenAI-compatible /v1/chat/completions request whose messages array contains no message with role: "user" (only system / assistant / tool messages), models served with a strict chat template that raises an exception in that case return HTTP 500 and the request fails. The failure is deterministic and reproducible; it surfaces as a Jinja exception from the upstream llama-server process:

text
Copy
Error: Jinja Exception: No user query found in messages.
This breaks the agentic loop on the affected model: the turn never completes, the chat task dies, and (depending on retry behaviour) the error can be persisted on the turn.

Steps to reproduce
Run a model with a strict Jinja chat template that raises when no user query is present. Any of the Qwen 3.x / Qwen3.5 family templates (and similar) contain the guard:

jinja
Copy
{%- if not messages or not messages[-1]... %}
{{- raise_exception('No user query found in messages.') }}
Reproduced with Qwen3.8-Orca-27B-Instruct (also seen in the wild with Qwen3.5-35B-A3B and other Qwen 3.x variants) served via llama-server --jinja behind llama-swap.

Connect cptr to that model (OpenAI-compatible endpoint).

Drive the model into an agentic turn where the outgoing request's messages array ends without a trailing user-role message — i.e. the conversation state at that moment is [system, assistant(+tool_calls), tool, tool, ...] with no user message anywhere (or no final user message). This can happen after context compaction / when the loop continues from a tool-result state.

Observe the upstream 500.

Upstream log (from llama-swap / llama-server)
text
Copy
Sep 26 21:52:45 Kurobako llama-swap[3194304]:
operator(): got exception: {"error":{"code":500,"message":"\n------------\nWhile
executing CallExpression at line 100, column 24 in source:\n...lti_step_tool %}
↵ {{- raise_exception('No user query found in messages.') }}
↵...\n ^\nError: Jinja Exception:
No user query found in messages.","type":"server_error"}}
Sep 26 21:52:45 Kurobako llama-swap[3194304]:
[WARN] non-200 response, recording partial metrics: status=500, path=/v1/chat/completions
Sep 26 21:52:45 Kurobako llama-swap[3194304]:
[INFO] Request ::1 "POST /v1/chat/completions HTTP/1.1" 500 333 "python-httpx/0.28.1" 24.949638ms
Note the python-httpx user agent — this is cptr's own outbound HTTP client.

Expected behaviour
The agentic loop should either:

always guarantee the outgoing messages array contains a valid user-role message (or a final user message) before calling the model, or
degrade gracefully (retry / recover) when the model's strict template rejects the payload, instead of the whole turn 500-ing.
A client that speaks the OpenAI chat-completions API should not be able to trip a server-side Jinja template guard by virtue of the shape of its conversation state.

Workaround
A small reverse proxy that injects a placeholder user message when none is present fixes the problem for the affected models. Running this in front of llama-swap and pointing cptr at the proxy makes the 500 disappear.

user_guard_proxy.py:
user_guard_proxy.py

Environment
cptr version: 0.9.21
Model: Qwen3.8-Orca-27B-Instruct, Ornith 1.5 35B A3B. (strict Jinja template)
Serving: llama-server --jinja behind llama-swap
OS: Linux x86_64 (Arch-based)

Related
Qwen3.5-35B-A3B "tool calling chat template is broken" discussion (Hugging Face): the same raise_exception('No user query found in messages.') guard.
r/LocalLLaMA "Trouble with Qwen 3.5 with LMStudio" — same guard tripping when the last message is a tool message.
Facebook Local LLM group: "no user query found in messages" firing once the conversation accumulates enough tool-call history.

Activity

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