{"record":{"id":"a97dd9028dc3f4ed","repo":"sgl-project/sglang","slug":"chunk-plan-plan-does-not-cover-the-incoming-inc","errorCode":null,"errorMessage":"chunk plan {plan} does not cover the incoming {incoming.shape[2]} frames","messagePattern":"chunk plan (.+?) does not cover the incoming (.+?) frames","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/multimodal_gen/runtime/pipelines_core/stages/model_specific_stages/sana_wm/streaming.py","lineNumber":269,"sourceCode":"        pcfg = server_args.pipeline_config\n        device = get_local_torch_device()\n        target_dtype = PRECISION_TO_TYPE.get(\n            getattr(pcfg, \"dit_precision\", \"bf16\"), torch.bfloat16\n        )\n        if batch.session is None:\n            raise ValueError(\"SANA-WM realtime denoising requires a realtime session\")\n        state = get_realtime_causal_dit_state(batch.session)\n        if batch.block_idx == 0 and state.latents is not None:\n            state.dispose()  # session restart on chunk 0 (mirrors the base stage)\n\n        sc = self._resolve_stream_conditioning(\n            batch, server_args, device=device, target_dtype=target_dtype\n        )\n        sampler_cfg = sc.sampler_cfg\n        incoming = batch.latents.to(device=device, dtype=target_dtype).clone()\n        plan = list(batch.extra.get(\"sana_wm_chunk_plan\") or [incoming.shape[2]])\n        if sum(plan) != incoming.shape[2]:\n            raise ValueError(\n                f\"chunk plan {plan} does not cover the incoming {incoming.shape[2]} frames\"\n            )\n        if state.scheduler is None:\n            state.scheduler = FlowMatchEulerDiscreteScheduler(shift=1.0)\n        kv_cache = state.kv_cache\n        if kv_cache is None:\n            # SANA-WM stores its heterogeneous per-block 10-slot stream cache in\n            # the generic causal DiT kv_cache slot.\n            kv_cache = []\n            state.kv_cache = kv_cache\n\n        # Device-only move, NO dtype cast: Module.to(dtype=...) would cast the\n        # DiT's complex RoPE buffers to real, discarding the imaginary part\n        # (parity root cause #1). No use_declared_component round-trip either —\n        # the DiT stays device-resident for the session's lifetime.\n        transformer = self.transformer.to(device=device).eval()\n        num_blocks = len(transformer.blocks)\n        _dump_dir = parity_probe.probe_dir(parity_probe.ENV_RT_DUMP)","sourceCodeStart":251,"sourceCodeEnd":287,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/multimodal_gen/runtime/pipelines_core/stages/model_specific_stages/sana_wm/streaming.py#L251-L287","documentation":"The realtime path reads a chunk plan (list of per-chunk frame counts) from batch.extra['sana_wm_chunk_plan'], defaulting to a single chunk covering all frames. The plan's entries must sum exactly to the incoming latents' temporal size; otherwise the autoregressive chunking is inconsistent with the data.","triggerScenarios":"A latent-preparation stage that emits N frames but writes a stale or miscounted chunk plan (e.g. plan built for a different chunk length after a config change, or plan not updated after trimming/padding frames).","commonSituations":"Changing num_frame_per_block between the prep stage and the denoiser; reusing batch.extra across retries where latents were regenerated with a different length; off-by-one in plan construction.","solutions":["Recompute sana_wm_chunk_plan from the actual latents at prep time: sum(plan) == latents.shape[2]","Align num_frame_per_block config across all streaming stages in the same run","Omit the plan key to use the default single-chunk fallback when appropriate"],"exampleFix":"# before\nbatch.extra[\"sana_wm_chunk_plan\"] = [n_chunk] * k  # stale counts\n# after\nplan, rem = [], latents.shape[2]\nwhile rem:\n    take = min(n_chunk, rem); plan.append(take); rem -= take\nbatch.extra[\"sana_wm_chunk_plan\"] = plan","handlingStrategy":"validation","validationCode":"plan = list(batch.extra.get(\"sana_wm_chunk_plan\") or [batch.latents.shape[2]])\nassert sum(plan) == batch.latents.shape[2], (plan, batch.latents.shape)","typeGuard":"null","tryCatchPattern":"try:\n    resp = stage.forward(batch, server_args)\nexcept ValueError as e:\n    if \"chunk plan\" in str(e):\n        batch.extra.pop(\"sana_wm_chunk_plan\", None)  # fall back to single-chunk default\n        resp = stage.forward(batch, server_args)\n    else:\n        raise","preventionTips":["Generate the chunk plan from the same tensor it will chunk","Share num_frame_per_block config across streaming stages","Drop stale extra keys when regenerating latents"],"tags":["sana-wm","streaming","chunk-plan","frame-count","valueerror"],"backgroundTag":"config-state-mismatch","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}