Skip to content

spec: mtmd image chunks leave positional holes in the draft KV cache -> llama_decode(ctx_dft) rc=-1 -> HTTP 500 with draft-dflash (CUDA); zero-fill patch crash-eliminating but no speculative benefit on images #27408

Description

@indubwestep

Summary

With --spec-type draft-dflash and an mmproj model, every image request stalls (~500 s) and then returns HTTP 500. Text-only requests with the same configuration work and speculate correctly. Root cause: a positional hole in the draft KV cache — mtmd image chunks advance the target context without being mirrored into the 1D draft KV cache, so llama_decode(ctx_dft) rejects the non-consecutive positions.

A community fix (z-lab#1, +74/−5 in common/speculative.cpp, zero-fills the hole) is crash-eliminating on CUDA in our testing (7/7 image regression + stability PASS, HTTP 500 gone) but provides no speculative benefit on image requests (draft/accept counters stay 0 for images; tolerated draft: llama_decode returned -1 is still logged), and a general position-gap case (non-consecutive positions across requests) can still terminate the server. Nothing here is claimed as fixed upstream.

Environment

  • llama.cpp at PR spec : add DFlash2 support (local convolution + candidate selector) #27342 head: 5ecbe1ac17ec0484c5b44af0bd580cdc9c428ed4 (local build, MSVC 19.44, CUDA 12.4, sm_86)
  • OS: Windows 11 x86_64
  • GPU: NVIDIA RTX 3090 Ti 24 GB
  • Target: empero-ai/Qwen3.8-27B-Ridge-GGUF Qwen3.8-27B-Ridge-3.7bpw.gguf (IQ2_M) + mmproj-Qwen3.8-27B-BF16.gguf
  • Draft: incoai/Qwen3.8-27B-DFlash2-GGUF Qwen3.8-27B-DFlash2-Q4_K_M.gguf (1,143,006,752 bytes; SHA-256 18a380efc9b7ed8d88677fc895f5c11ae170653434ee378f7348f715c14d0594)

Failure log (image request, draft-dflash)

init: the tokens of sequence 0 in the input batch have inconsistent sequence positions:
 - the last position stored in ... KV cache ... is X
 - the tokens for sequence 0 ... have a starting position of Y
 it is required that the sequence positions remain consecutive: Y = X + 1
process: llama_decode(ctx_dft) failed rc=-1 (n_tokens=..., offset=...)
srv  update_slots: decode() failed: failed to process speculative batch   -> HTTP 500

Repro (llama-server)

llama-server --model <target> --mmproj <mmproj> --ctx-size 8192 --n-gpu-layers 999 \
  --flash-attn on --cache-type-k q4_0 --cache-type-v q4_0 --batch-size 256 \
  --ubatch-size 128 --threads 8 --parallel 1 --temp 0 --seed 42 \
  --spec-draft-model <draft> --spec-type draft-dflash \
  --spec-draft-ngl 999 --spec-draft-n-max 7 --spec-draft-p-min 0

Send any image in a chat-completions request → observe the ~500 s stall then HTTP 500. The same request with --spec-type none (or without mmproj) succeeds.

Root cause

  • mtmd image chunks are decoded directly into the target context by the mtmd helper and never pass common_speculative_process() (the PR spec : add DFlash2 support (local convolution + candidate selector) #27342 DFlash path) — the draft KV cache therefore has a positional hole (M-RoPE targets use non-linear image positions; even a single-ubatch chunk injects draft rows at bogus positions, per the z-lab fix description).
  • The 1D draft KV cache requires consecutive positions; the hole fails the consecutive check in llama_decode(ctx_dft) (rc=-1) → HTTP 500.

Upstream corroboration

Evaluation of the z-lab fix on CUDA (this report)

  • Patch: dflash: zero-fill draft-cache holes left by mtmd chunks and reused prefixes z-lab/llama.cpp-fork#1 (+74/−5, single file common/speculative.cpp); skips embedding batches in the DFlash process() and zero-fills the draft-cache positional hole (log: draft cache hole for seq 0: [46, 62) - seeding with zero features).
  • Result: HTTP 500 gone. 7/7 image regression PASS (full-length generations, stable VRAM), stability addendum PASS (3x repeatability, text-after-image, restart resilience). Crash-eliminating on CUDA.
  • Residual on CUDA: image requests still log 14+ tolerated draft: llama_decode returned -1 position-mismatch failures per request and run without speculative benefit — draft/accept counters stay 0 across all image tests, so images decode at full target speed (no vision speedup). Pure-text requests with the same binary speculate at ~100% acceptance.
  • Remaining blocker: cross-request position gaps (non-consecutive positions spanning slots/requests) still terminate the server (exit -1). The z-lab patch covers the single mtmd hole only, not general position-gap handling.

Ask

  1. Is there a path to a general fix (draft-KV position remapping / gap support) so DFlash2 can also speculate on image requests?
  2. Maintainer guidance on where gap-tolerant draft-KV handling belongs (draft-KV refactor vs process() special-casing)?

Cross-links: #26108 (same failure, Qwen3.6-27B), PR #27342 (DFlash2; comment thread confirms M-RoPE rejection), z-lab#1 (zero-fill fix, Metal-validated), companion issue CUDA batched-verification parity.

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