{"record":{"id":"8a702f012ebc64e4","repo":"sgl-project/sglang","slug":"missing-captured-kv-on-attr","errorCode":null,"errorMessage":"missing captured KV on {attr}","messagePattern":"missing captured KV on (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"python/sglang/multimodal_gen/runtime/pipelines_core/stages/model_specific_stages/sana_wm/streaming_refiner.py","lineNumber":77,"sourceCode":"    if mode == \"pre_rope\":\n        attr, clear_attr = \"_kv_cache_capture\", \"_cached_kv_pre\"\n    elif mode == \"post_rope\":\n        attr, clear_attr = \"_tf_capture_kv\", \"_cached_kv_post\"\n    else:\n        raise ValueError(f\"unsupported capture mode: {mode}\")\n    for block in transformer.transformer_blocks:\n        setattr(block.attn1, attr, bool(enable))\n        if enable and hasattr(block.attn1, clear_attr):\n            setattr(block.attn1, clear_attr, None)\n\n\ndef collect_captured_kv_from_blocks(transformer: nn.Module, mode: str):\n    attr = \"_cached_kv_pre\" if mode == \"pre_rope\" else \"_cached_kv_post\"\n    out = []\n    for block in transformer.transformer_blocks:\n        cached = getattr(block.attn1, attr, None)\n        if cached is None:\n            raise RuntimeError(f\"missing captured KV on {attr}\")\n        out.append(cached)\n        setattr(block.attn1, attr, None)\n    return out\n\n\n# --------------------------------------------------------------------------- #\n# Absolute-position RoPE (port of refiner.py:721-750)\n# --------------------------------------------------------------------------- #\ndef build_rotary_emb_for_absolute_positions(\n    *, transformer, batch_size, frame_positions, height, width, device, fps\n):\n    rope = transformer.rope\n    patch_size_t = int(rope.patch_size_t)\n    patch_size = int(rope.patch_size)\n    f_positions = torch.tensor(frame_positions, dtype=torch.float32, device=device)\n    if patch_size_t > 1:\n        f_positions = f_positions[::patch_size_t]\n    grid_h = torch.arange(0, height, patch_size, dtype=torch.float32, device=device)","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/multimodal_gen/runtime/pipelines_core/stages/model_specific_stages/sana_wm/streaming_refiner.py#L59-L95","documentation":"collect_captured_kv_from_blocks reads the cached (K, V) tensors each attention block stored under _cached_kv_pre or _cached_kv_post (depending on mode). If any block's cache is empty, capture was never enabled or the attention forward did not run/populate it, and the collector raises this RuntimeError.","triggerScenarios":"Calling collect_captured_kv_from_blocks before running a forward pass with capture enabled (set_capture_flag_on_blocks not called, or called with a different mode than the collect call); a custom attention implementation ignoring the capture flag.","commonSituations":"Mismatched mode strings between enable and collect calls; collecting twice (first collect clears the attrs via setattr None); a forward that short-circuited before attention executed.","solutions":["Always pair set_capture_flag_on_blocks(..., enable=True, mode=M) with a forward, then collect_captured_kv_from_blocks(..., mode=M) with the same M","Do not collect twice without re-running the forward — the first collect clears the caches","Ensure the attention module honors the capture attrs (custom kernels may bypass them)"],"exampleFix":"# before\nset_capture_flag_on_blocks(tf, enable=True, mode=\"pre_rope\")\nkvs = collect_captured_kv_from_blocks(tf, mode=\"post_rope\")  # mode mismatch -> None\n# after\nset_capture_flag_on_blocks(tf, enable=True, mode=\"pre_rope\")\n_ = tf(...)\nkvs = collect_captured_kv_from_blocks(tf, mode=\"pre_rope\")","handlingStrategy":"validation","validationCode":"set_capture_flag_on_blocks(tf, enable=True, mode=mode)\n_ = tf(*model_inputs)  # forward must run to populate caches\nattrs_ok = all(getattr(b.attn1, \"_cached_kv_pre\" if mode == \"pre_rope\" else \"_cached_kv_post\", None) is not None for b in tf.transformer_blocks)","typeGuard":"def kv_captured(tf, mode: str) -> bool:\n    attr = \"_cached_kv_pre\" if mode == \"pre_rope\" else \"_cached_kv_post\"\n    return all(getattr(b.attn1, attr, None) is not None for b in tf.transformer_blocks)","tryCatchPattern":"try:\n    kvs = collect_captured_kv_from_blocks(tf, mode)\nexcept RuntimeError:\n    set_capture_flag_on_blocks(tf, enable=True, mode=mode)\n    _ = tf(*model_inputs)\n    kvs = collect_captured_kv_from_blocks(tf, mode)","preventionTips":["Pair enable(mode) -> forward -> collect(mode) as one code path","Never collect twice without re-running the forward","Use one shared mode constant for both calls"],"tags":["sana-wm","streaming-refiner","kv-capture","runtimeerror","state-lifecycle"],"backgroundTag":"missing-captured-state","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}