vLLM Explained

Troubleshooting vLLM: symptom, cause and fix

AdvancedvLLM conceptsabout 16 min read + 3 min deep dives

Code version: vLLM @ 3e44c0d · 2026-10-22

In one sentence

Find the phase and the process that failed. The first traceback in that process's log names the cause; this page maps it to a fix.

The idea#

A vllm serve problem shows up in one of three phases:

  1. Start-up: the log never reaches "Application startup complete.". The cause is almost always in the configuration, the memory or the environment.
  2. Serving: the server runs, but requests fail (HTTP errors) or are slow. The cause is in the request, the load or a dead engine.
  3. Output: HTTP 200, but the content is wrong: tool calls as plain text, rejected JSON schemas, changed quality, a plugin without effect.

The second rule: several processes write to one log (The process and thread architecture of vllm serve). Each line has a prefix, for example (APIServer pid=...) or (EngineCore pid=...). When the engine core fails, the API server only reports that it failed. The real cause is the first traceback in the engine core or worker lines above it.

An everyday comparison. Think of the triage desk of a hospital. The nurse first asks when the problem started and where it hurts, and only then calls the right specialist. The comparison stops at the patient. In vLLM, the "patient" writes its own detailed notes (the tracebacks and log lines). This page tells you which note to read first.

The picture#

Figure: the first question is the phase; each card names the first thing to check. The sections below give the cause in the code and the fix.
Open full size ↗

Read the lane that matches the phase, from top to bottom. The cards are ordered by how often each problem shows up at start-up or in serving (a judgement, not a measurement). Each card has a step below with the code lines.

In the code#

Step 1: read the right log line#

When the engine core process fails during start-up, it logs the traceback with "EngineCore failed to start." and re-raises (vllm/v1/engine/core.py:1570-1572). A worker process logs "WorkerProc failed to start." (vllm/v1/executor/multiproc_executor.py:957). The API server then raises its own error, which points back to them:

            raise RuntimeError(
                "Engine core initialization failed. "
                "See root cause above. "
                f"Failed core proc(s): {finished}"
                + (
                    f", failed frontend proc(s): {failed_frontend_procs}"
                    if failed_frontend_procs
                    else ""
                )
            )

So scroll up from "Engine core initialization failed. See root cause above." to the first traceback of an EngineCore or Worker line. For more detail, set VLLM_LOGGING_LEVEL=DEBUG (default INFO, vllm/envs.py:860). The vLLM docs also suggest CUDA_LAUNCH_BLOCKING=1 for a failing kernel and NCCL_DEBUG=TRACE for NCCL (docs/usage/troubleshooting.md:33-41).

Step 2: start-up runs out of memory#

Four errors look alike, but they happen at four different points (The KV cache manager: blocks, the block pool and memory sizing and Tuning vLLM: throughput and latency knobs, their real defaults and what each one does explain the memory profile):

Message (start)CauseFix
"Free memory on device ... is less than desired GPU memory utilization"another process uses the GPU; total × gpu_memory_utilization is not free (vllm/v1/worker/utils.py:761-770)lower --gpu-memory-utilization, or stop the other process
torch.OutOfMemoryError during the profile runthe dummy forward pass with max_num_batched_tokens tokens does not fit (vllm/v1/worker/gpu/model_runner.py:1022-1027)lower --max-num-batched-tokens; TP or quantization for big weights
"No available memory for the cache blocks."weights, activations and the graph estimate use all the requested memory (vllm/v1/core/kv_cache_planning.py:56-64)raise --gpu-memory-utilization; lower --max-num-batched-tokens; --enforce-eager
torch.OutOfMemoryError during graph capturethe real graph memory is larger than the estimatelower --gpu-memory-utilization a little, or --enforce-eager

--enforce-eager saves the graph memory: the estimate is 0 and no graph is captured (vllm/v1/worker/gpu_worker.py:704-708, vllm/v1/worker/gpu_worker.py:983-985). It costs speed in every step (torch.compile and CUDA graphs in vLLM).

After a successful start, the worker logs the measured parts and a suggested fixed KV size. The text names the flag --kv-cache-memory=N, but the flag is --kv-cache-memory-bytes (vllm/v1/worker/gpu_worker.py:1046-1053, vllm/engine/arg_utils.py:1384-1386). The vLLM docs use the short name too (docs/configuration/optimization.md:24). The short name also works on the command line (derived, not run with vLLM). Python's argparse accepts an unambiguous prefix of a long option, and no vLLM parser sets allow_abbrev=False. FlexibleArgumentParser does not change the prefix matching (vllm/utils/argparse_utils.py:313-502). --kv-cache-memory-bytes is the only flag that starts with --kv-cache-memory.

Step 3: max_model_len is too large#

Two different errors stop the start-up:

  1. Larger than the model allows. "User-specified max_model_len (N) is greater than the derived max_model_len (...)" when --max-model-len exceeds the model config. VLLM_ALLOW_LONG_MAX_MODEL_LEN=1 turns this into a warning, with a caution about NaN outputs for RoPE models (vllm/config/model.py:2656-2683).
  2. Larger than the KV cache can hold. The message starts "To serve at least one request with the model's max seq len (N), (X GiB KV cache is needed ...". It ends with "... the estimated maximum model length is M." (vllm/v1/core/kv_cache_planning.py:68-87).

For the second error: set --max-model-len M, or --max-model-len auto, which fits the length to the memory and logs "Auto-fit max_model_len: reduced from ..." (vllm/v1/core/kv_cache_planning.py:1584-1587, vllm/v1/core/kv_cache_planning.py:1401-1410). The other fixes are more KV memory (Step 2) or --kv-cache-dtype fp8 (Tuning vLLM: throughput and latency knobs, their real defaults and what each one does). The playground on Tuning vLLM: throughput and latency knobs, their real defaults and what each one does computes the same estimate.

Step 4: start-up is slow#

After the model loads, the engine core profiles, compiles and captures graphs. Then it logs "init engine (profile, create kv cache, warmup model) took X s (compilation: Y s)" (vllm/v1/engine/core.py:394-400). A cold start compiles with torch.compile; a warm start loads the saved result and logs "Directly load AOT compilation from path ..." (vllm/compilation/decorators.py:326-330). The cache lives under VLLM_CACHE_ROOT (vllm/envs.py:713-718; Tour: Start-up of vllm serve).

Step 5: "EngineCore failed to start." and the start-up timeouts#

Besides memory and length errors, three causes are common:

Timeouts at start-up depend on the shape:

Step 6: the engine dies while serving#

An error in the loop of the engine core makes it log "EngineCore encountered a fatal error.". Then it sends a dead message to the front end and exits (vllm/v1/engine/core.py:1573-1576). The front end then raises EngineDeadError (vllm/v1/engine/core_client.py:581-584). The result is engine death:

With worker processes, each execute_model call waits at most VLLM_EXECUTE_MODEL_TIMEOUT_SECONDS (default 300) for the workers (vllm/envs.py:1859-1863, vllm/v1/executor/multiproc_executor.py:350). Then it raises TimeoutError("RPC call to execute_model timed out.") (vllm/v1/executor/multiproc_executor.py:443). This is an error in the engine core loop, so the engine dies as above (derived; with fault tolerance off, the default). The comment in envs.py says "only applies when TP > 1"; the code applies it to every multiprocessing executor, also with PP or DP. A hang inside a collective (NCCL) usually shows up as this timeout (Step 10).

Step 7: HTTP 400 and 404#

The error body names the cause and often the field (The OpenAI-compatible API). It has the form {"error": {"message": "...", "type": "BadRequestError", "param": "...", "code": 400}}. The common ones:

Status and messageCauseWhere
400 "This model's maximum context length is N tokens. However, your prompt contains M input tokens ..."the prompt is longer than max_model_lenvllm/renderers/params.py:518-538
400 "Stream options can only be defined when `stream=True`."stream_options without streamvllm/entrypoints/openai/chat_completion/protocol.py:847-855
400 "tool_choice=... requires --tool-call-parser to be set"tools without the parser flags (Step 9)Tool and reasoning parsers: from model text to tool_calls and reasoning
400 "Structured output processing only supports one backend per engine."Step 9vllm/v1/structured_output/__init__.py:143-150
404 "The model ... does not exist."model is not a served name or a loaded LoRA namevllm/entrypoints/serve/engine/serving.py:75-84
503queue limits (--max-num-queued-reqs, --max-num-queued-tokens) are fullThe OpenAI-compatible server: launch modes, app, serving classes and streaming

A max_tokens larger than the rest of the context is not an error: the server lowers it to max_model_len − prompt length (vllm/entrypoints/serve/utils/api_utils.py:253-270). For a 404, check GET /v1/models: the served name is --served-model-name, else the model path (vllm/config/model.py:2330-2334).

Step 8: slow serving: preemption and prefix-cache misses#

Preemption. When running requests need more blocks than are free, the scheduler preempts one and recomputes it later (The V1 scheduler: one token budget, no phases). The scheduler writes no log line per preemption. The signs are the counter vllm:num_preemptions_total and "Preemptions: N" in the periodic log line (vllm/v1/metrics/loggers.py:738-745, vllm/v1/metrics/loggers.py:310-312). Watch the preemption rate: rate(vllm:num_preemptions_total[5m]). The vLLM docs still show a V0-style warning line for preemption (docs/configuration/optimization.md:33-35); the current code does not print it. Fix: more KV memory, or fewer running requests (Tuning vLLM: throughput and latency knobs, their real defaults and what each one does).

A low prefix-cache hit rate. The cache hits only when the hash chain of full blocks matches (Prefix caching in the engine core: hashes, lookup, caching, eviction and KV events). Hits break when:

  • the shared prefix is shorter than one block, or the difference starts inside the first block: only full blocks hit (vllm/v1/core/kv_cache_manager.py:264-266);
  • requests use different LoRA adapters, multimodal inputs or cache_salt values, which are part of the block hash (vllm/v1/core/kv_cache_utils.py:655-665);
  • a request asks for prompt_logprobs: it skips reading the cache (vllm/sampling_params.py:625-632);
  • the chat template puts a changing value (a date, an id) near the start of the prompt (general knowledge about templates);
  • you use a non-cryptographic hash (xxhash) without PYTHONHASHSEED. Then the hash seed is random per process, and tools that compare hashes across instances find no match (vllm/v1/core/kv_cache_utils.py:117-130).

Also note that requests that come back after a preemption are not in the exported prefix counters (Metrics and observability: how to watch a vLLM server).

Step 9: wrong output with HTTP 200#

Step 10: multi-node and NCCL hangs#

The vLLM docs have the details; this page points to them:

Deep dive#

The sections below give the full detail. Open them one at a time, or use Open all deep dives at the top of the page.

Deep dive

Worked example: a 24 GiB GPU and a 128K-context model

The per-token values are the same as in the Llama-3.1-8B example on Tuning vLLM: throughput and latency knobs, their real defaults and what each one does; the memory values are made up (illustrative). Assume a 24 GiB GPU with gpu_memory_utilization 0.92. The profile finds 15 GiB weights, 1 GiB other memory, a 2.5 GiB activation peak and a 0.5 GiB graph estimate. The KV cache needs 128 KiB per token, and max_model_len is 131,072 from the model config.

  1. Requested: ceil(24 × 0.92) = 22.08 GiB. Available: 22.08 − 16.0 − 2.5 − 0.5 = 3.08 GiB (vllm/v1/worker/gpu_worker.py:744-748).
  2. One block (16 tokens) is 2 MiB, so there are 1,576 blocks (vllm/v1/core/kv_cache_planning.py:1610-1611).
  3. The check keeps one block back: 3.08 GiB − 2 MiB. One request of 131,072 tokens needs 8,192 blocks = 16 GiB, which does not fit (vllm/v1/core/kv_cache_planning.py:1572-1582).
  4. The error estimates the largest length that fits: 1,575 blocks × 16 = 25,200 tokens (vllm/v1/core/kv_cache_planning.py:1288-1334).
  5. Fixes, each with its effect:
    • --max-model-len 25200 or auto: the server starts with a maximum concurrency of 1.0x.
    • --max-model-len 8192: 1,576 / 512 = 3.08x.
    • --kv-cache-dtype fp8: a block is 1 MiB, so 3,153 blocks fit and the estimate becomes 50,432 tokens.
    • A quantized checkpoint frees weight memory.
Deep dive

Error messages and where they come from

Message (start)ProcessWhere
"Engine core initialization failed. See root cause above."API servervllm/v1/engine/utils.py:1468-1477
"EngineCore failed to start." / "EngineCore encountered a fatal error."engine corevllm/v1/engine/core.py:1570-1574
"WorkerProc failed to start."workervllm/v1/executor/multiproc_executor.py:957
"Timed out waiting for engine core processes to start."API server (N > 1)vllm/v1/engine/core_client.py:760-768
"Did not receive response from front-end process within 5 minutes"engine corevllm/v1/engine/core.py:1408-1413
"RPC call to execute_model timed out."engine core (mp executor)vllm/v1/executor/multiproc_executor.py:443
"Error in memory profiling. Initial free memory ..."worker: another process freed GPU memory during the profilevllm/v1/worker/gpu_worker.py:723-731
"max_num_batched_tokens (N) is smaller than max_model_len (M)."front end: chunked prefill offvllm/config/scheduler.py:318-329
"max_num_batched_tokens (N) must be greater than or equal to max_num_seqs (M)."front endvllm/config/scheduler.py:331-335
"Error: --enable-auto-tool-choice requires --tool-call-parser"CLIvllm/entrypoints/common/cli_args.py:523-525
Deep dive

Environment variables for troubleshooting

VariableDefaultEffect
VLLM_LOGGING_LEVELINFO (vllm/envs.py:860)DEBUG shows plugin discovery, idle log lines and more
VLLM_ENGINE_READY_TIMEOUT_S600 (vllm/envs.py:812-814)start-up wait with more than one API server
VLLM_EXECUTE_MODEL_TIMEOUT_SECONDS300 (vllm/envs.py:1861-1863)wait for workers per step (mp executor)
VLLM_KEEP_ALIVE_ON_ENGINE_DEATH0 (vllm/envs.py:1107-1109)keep the HTTP server after engine death
VLLM_ALLOW_LONG_MAX_MODEL_LEN0 (vllm/envs.py:1114-1117)allow a length above the model config (risky)
VLLM_PLUGINSunset = all (vllm/envs.py:1163-1167)filter plugins by name
VLLM_MEMORY_PROFILER_ESTIMATE_CUDAGRAPHS1 (vllm/envs.py:2217-2219)0 removes the graph estimate from the KV budget
VLLM_CACHE_ROOT~/.cache/vllm or under XDG_CACHE_HOME (vllm/envs.py:713-718)compile and model-info caches

All variables are on Environment variables.

What can go wrong#

Troubleshooting itself has traps:

  • Reading the last error instead of the first. The API server's "Engine core initialization failed" is a consequence; the cause is above it (Step 1).
  • Copying the suggested --kv-cache-memory=N to another machine. The value is valid only for the same GPU with the same free memory (Step 2).
  • Trusting old docs for preemption. The V0 warning line in the docs does not exist in the current code (Step 8).
  • Raising VLLM_ENGINE_READY_TIMEOUT_S with one API server. It has no effect there (Step 5).
  • Setting VLLM_KEEP_ALIVE_ON_ENGINE_DEATH=1 in production. The server stays up but answers 500 to every request; the orchestrator does not restart it unless it checks /health (Step 6).

Why it matters. Most vLLM problems leave a precise message in the right process log. Know the phase, the process and the line of code that wrote the message. Then a fix usually takes minutes instead of hours.

Check yourself#

  1. 1
    The log ends with "Engine core initialization failed. See root cause above." Where is the cause?
    easy
    Show answer

    In the first traceback of an EngineCore or worker line above it. The engine core logs "EngineCore failed to start." with the traceback and re-raises (vllm/v1/engine/core.py:1570-1572); the API server only reports that the process exited (vllm/v1/engine/utils.py:1468-1477).

  2. 2
    A client gets HTTP 404 "The model ... does not exist". What do you check?
    easy
    Show answer

    Compare the model field of the request with GET /v1/models. It must be a served name (--served-model-name, else the model path) or a loaded LoRA name (vllm/entrypoints/serve/engine/serving.py:75-84, vllm/config/model.py:2330-2334).

  3. 3
    After an hour of traffic, every request gets HTTP 500 and the server exits a few seconds later. What happened, and what keeps it from exiting?
    medium
    Show answer

    The engine core died (engine death): the front end raises EngineDeadError, requests get 500, and the watchdog stops the server within about 5 s (vllm/entrypoints/serve/exception_handling/error_response.py:68-72, vllm/entrypoints/launchers/launcher.py:314-337). VLLM_KEEP_ALIVE_ON_ENGINE_DEATH=1 keeps it up, but the engine stays dead; read the engine core traceback instead.

  4. 4
    Prefix caching is on, every request has the same 10-token system prompt, and the hit rate is 0 %. Why?
    medium
    Show answer

    Only full blocks can hit, and a block has 16 tokens by default (vllm/v1/core/kv_cache_manager.py:264-266, vllm/config/cache.py:182). A shared prefix of 10 tokens never fills a block, so no block hash matches.

  5. 5
    On a 24 GiB GPU, start-up says "the estimated maximum model length is 25200". You set --max-model-len 25200. How many full-length requests fit at once, and how could you get about twice as many?
    hard
    Show answer

    One: the usable blocks (1,575) cover exactly 25,200 tokens, and the log shows a maximum concurrency of about 1.0x (vllm/v1/core/kv_cache_utils.py:1031-1047; deep dive). --kv-cache-dtype fp8 halves the bytes per token (vllm/v1/kv_cache_interface.py:571-575), so about twice as many tokens fit; check the output quality after the change.