<!-- llms-explorer concept facts · https://llms-explorer.com/tree/jinja-templates-failing-on-llama-cpp-engine-but/ · pack 2026-10-05 · ~1910 tokens -->

# Jinja templates failing on llama.cpp engine but passing in Python

> llama-server returns the engine exception as an HTTP 500 body that names the node, line and column, for example "While executing FilterExpression at line 18, column 34".

Parent: [Mac local LLMs: llama.cpp internals](https://llms-explorer.com/tree/mac-local-llms-llama-cpp-internals/) · 1 facets · 30 facts · page: https://llms-explorer.com/tree/jinja-templates-failing-on-llama-cpp-engine-but/

## Facts

- llama-server returns the engine exception as an HTTP 500 body that names the node, line and column, for example "While executing FilterExpression at line 18, column 34". — [source](https://api.github.com/repos/ggml-org/llama.cpp/issues/21347)
- The engine types every value. A filter or test missing from the receiver type's table throws instead of coercing, so a template that passes a list to a string filter works only where Python is lenient. — [source](https://api.github.com/repos/ggml-org/llama.cpp/issues/21347)
- `Undefined` is a distinct type. `message['tool_calls'] is not none` is true for a missing key, because the test is a strict type check and `Undefined` is not `None`. — [source](https://api.github.com/repos/ggml-org/llama.cpp/issues/19130)
- Python Jinja2 accepts an undefined value as an empty iterable, so `undefined | length` works there. The C++ engine threw "filter 'length' for type Undefined" until a fix was promised on 2026-01-27. — [source](https://api.github.com/repos/ggml-org/llama.cpp/issues/19130)
- 2026-01-17, issue 18899: a request with a tool whose schema had `"default": false` made the gpt-oss template fail with "Unknown (built-in) filter 'tojson' for type Boolean" on build 7761. On master read 2026-10-04 `tojson` is registered for int, float, string, bool, array, object and none, so this class is fixed. — [source](https://api.github.com/repos/ggml-org/llama.cpp/issues/18899)
- 2026-01-27, issue 19130: the Apriel 1.6 Thinker GGUF template failed on `length` of undefined. A user reported it had worked before a llama.cpp change; maintainer CISC said it never worked with the new engine and that the template should use `{%- if message['tool_calls'] -%}`. — [source](https://api.github.com/repos/ggml-org/llama.cpp/issues/19130)
- 2026-04-03, issue 21347: Gemma 4 GGUF (Unsloth) returned 500 "Unknown (built-in) filter 'upper' for type Array" because `value['type'] | upper` received a list; a user's edited template file worked around it. — [source](https://api.github.com/repos/ggml-org/llama.cpp/issues/21347)
- 2026-06-24, PR 24971 proposed honouring `case_sensitive` in `sort` and `dictsort`; it was closed on 2026-06-25 without merging. The FIXME "sorting is currently always case sensitive" is still in `value.cpp` on master. — [source](https://raw.githubusercontent.com/ggml-org/llama.cpp/master/common/jinja/value.cpp)
- 2026-08-12, issue 26974: render time grew quadratically with tool count; PR 27034 fixed it on 2026-08-14. — [source](https://api.github.com/repos/ggml-org/llama.cpp/pulls/27034)
- Silent divergence: `sort(case_sensitive=false)` and `dictsort(case_sensitive=false)` are accepted and ignored, so the rendered order differs from Python with no error. — [source](https://api.github.com/repos/ggml-org/llama.cpp/pulls/24971)
- A tool schema whose `type` is a list, such as a union with null, reaches a template macro that assumed a string. A template tested only with scalar types passes in Python tests and fails in llama-server. — source: `asserted`
- The 21347 reporter saw the failure on a plain "Hello" request with no tools. The source does not explain why a tool-formatting macro ran; probes that render synthetic tools at template load are a possible cause. — [source](https://api.github.com/repos/ggml-org/llama.cpp/issues/21347)
- Render cost is a failure mode on its own: before PR 27034, a template that loops over `tools` cost 0.43 s of CPU at 20 tools by 20 properties and 128 s at 100 by 100. — [source](https://api.github.com/repos/ggml-org/llama.cpp/issues/26974)
- A first-time fix attempt for PR 27034 that omitted a `++w != r` guard self-moved a string and silently dropped 39 bytes of prompt. — [source](https://api.github.com/repos/ggml-org/llama.cpp/pulls/27034)
- Who owns the failure. CISC (maintainer): the template is broken and should be fixed; altering engine semantics to cope would break other templates. A user (coder543): the same GGUF worked before a llama.cpp update, so the update regressed it. The two statements are not reconciled in the thread; the maintainer then added Undefined-as-iterable behaviour anyway. — [source](https://api.github.com/repos/ggml-org/llama.cpp/issues/19130)
- How issue 18899 and issue 21347 were fixed (which commit). The issues closed without a linked fix in the data read. — source: `asserted`
- Whether a Python-side conformance check (render every shipped GGUF template through both engines) exists in CI; PR 18462 reports a 370-template corpus run only. — source: `asserted`
- A llama-server template exception returns HTTP 500 whose message names the node type, line and column and the error text. — [source](https://api.github.com/repos/ggml-org/llama.cpp/issues/21347)
- Gemma 4 GGUF template failed with "Unknown (built-in) filter 'upper' for type Array" on llama.cpp build 8642. — [source](https://api.github.com/repos/ggml-org/llama.cpp/issues/21347)
- The 21347 failure also occurred for gemma-4-E4B-it and gemma-4-E2B-it according to commenters. — [source](https://api.github.com/repos/ggml-org/llama.cpp/issues/21347)
- A tool schema with `"default": false` produced "Unknown (built-in) filter 'tojson' for type Boolean" with the gpt-oss template on build 7761. — [source](https://api.github.com/repos/ggml-org/llama.cpp/issues/18899)
- On master as of 2026-10-04 `tojson` is registered on bool, none, array and object value types as well as int, float and string. — [source](https://raw.githubusercontent.com/ggml-org/llama.cpp/master/common/jinja/value.cpp)
- The Apriel 1.6 template guard `message['tool_calls'] is not none and message['tool_calls']|length > 0` failed because a missing key is `Undefined`, which is not `None`. — [source](https://api.github.com/repos/ggml-org/llama.cpp/issues/19130)
- Maintainer CISC stated Python Jinja2 accepts `undefined` as an iterable and said he would make the C++ engine do likewise. — [source](https://api.github.com/repos/ggml-org/llama.cpp/issues/19130)
- Both `sort` and `dictsort` in `value.cpp` read `case_sensitive` and still carry a FIXME that sorting is always case sensitive. — [source](https://raw.githubusercontent.com/ggml-org/llama.cpp/master/common/jinja/value.cpp)
- PR 24971 (honour `case_sensitive`) was closed on 2026-06-25 without merging. — [source](https://api.github.com/repos/ggml-org/llama.cpp/pulls/24971)
- Issue 26974 traced 57 percent of instructions in a render to the erase loop in `gather_string_parts`. — [source](https://api.github.com/repos/ggml-org/llama.cpp/issues/26974)
- Issue 26974 listed render times of 0.031 s (5x5), 2.425 s (40x30), 9.295 s (50x50) and 128.5 s (100x100) tools by properties for the Command-R tool-use template. — [source](https://api.github.com/repos/ggml-org/llama.cpp/issues/26974)
- PR 27034 found two independent quadratic terms, `string::append` returning by value and the erase loop, and both had to be fixed to reach linear growth. — [source](https://api.github.com/repos/ggml-org/llama.cpp/pulls/27034)
- A template that passes under Python `apply_chat_template` can still fail under llama-server when it relies on Undefined being iterable or on list-valued schema types. — source: `asserted`
