Field note · llama.cpp

Dropping --kv-unified silently turns a 262144-token server into a 65536-token one

llama-server divides the context size by the slot count unless --kv-unified is set: n_ctx_seq = n_ctx / n_seq_max. There is no error and no warning. The same command line that asks for --ctx-size 262144 --parallel 4 serves 65536 per sequence, and the only evidence is one line in the boot log. Long prompts then fail or shift in ways that look like a model problem rather than a configuration one.

Hardware
NVIDIA DGX Spark · GB10 (SM121) · aarch64 · 128 GB unified
Software
llama.cpp master · unsloth/Qwen3.8-Flash-Next-GGUF UD-IQ4_XS · llama-server, 4 slots

The silent division

src/llama-context.cpp:

if (cparams.kv_unified) { n_ctx_seq = n_ctx; }              // 262144 per slot
else                    { n_ctx_seq = n_ctx / n_seq_max; }  //  65536 per slot

--ctx-size is the size of the whole pool, not the size available to a request. Without --kv-unified, four slots each get a quarter of it. Nothing warns you. The flag you did not pass is the flag that would have kept the number you did pass.

The failure this produces is indirect, which is what makes it expensive: prompts that fit comfortably in the context you asked for start truncating or shifting, and the first suspicion lands on the model or the client rather than on a divide you never saw.

Confirming it

There is exactly one line of evidence, in the boot log:

init: initializing, n_slots = 4, n_ctx_slot = 262144, kv_unified = 'true'

n_ctx_slot is the number that matters. If it is n_ctx / n_slots, unified mode is off. Grep for it on every start; it costs nothing and it is the only place the truth is printed.

The caveat that comes with the fix

--kv-unified is not free capacity. Four slots at 262144 is a scheduling ceiling, not a guarantee — unified mode allocates one shared pool, so four simultaneous conversations all sitting near full context would oversubscribe it. What you get is the ability for any single request to use the whole context, not the ability for all four to do so at once.

For a prefill-bound workload that is the right trade anyway, and on this host the slot count went to 1 for an unrelated and larger reason: four slots scattered requests across slots by LRU and collapsed prefix-cache reuse from 86.7% to 1.2%. That story is in I turned off concurrency and the server got faster.

Related: the binary outlives the checkout

A neighbouring silent-configuration trap on the same server, worth the same defensive habit. build/ survives a git checkout, so switching branches and forgetting to rebuild leaves a binary from the previous branch — and the only runtime evidence is an unknown-architecture abort that never names the branch you needed. Probe the binary, not the source tree:

LIB_HITS=$(strings "$LIB" 2>/dev/null | grep -c "qwen4exp" || true)
(( LIB_HITS == 0 )) && { echo "stock master build — wrong binary"; exit 1; }

Use grep -c, not grep -q. Under set -o pipefail, grep -q exits on its first match, which SIGPIPEs strings (exit 141), and pipefail then reports a successful match as a failed pipeline — rejecting a perfectly good binary. -c consumes all of its input, so there is no early exit to signal.

Status
resolved
First seen
August 28, 2026