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.
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.