{"record":{"id":"896cc4fb8a352ca4","repo":"sgl-project/sglang","slug":"decoder-stage-channels-stage-idx-1-must-be-e","errorCode":null,"errorMessage":"decoder_stage_channels[{stage_idx + 1}] must be {expected}, got {stage_channels[stage_idx + 1]}.","messagePattern":"decoder_stage_channels\\[(.+?)\\] must be (.+?), got (.+?)\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/multimodal_gen/runtime/models/decoders/ltx_2_5_diffusion_decoder.py","lineNumber":661,"sourceCode":"    def __init__(self, config: LTX25DiffusionDecoderConfig) -> None:\n        super().__init__()\n        arch = config.arch_config\n        stage_channels = tuple(arch.decoder_stage_channels)\n        stage_depths = tuple(arch.decoder_stage_depths)\n        stage_kernels = tuple(tuple(k) for k in arch.decoder_stage_kernels)\n        upsample_strides = tuple(tuple(s) for s in arch.decoder_upsample_strides)\n        reductions = tuple(arch.decoder_upsample_channel_reductions)\n\n        if arch.decoder_model_output_type not in (\"x0\", \"v\"):\n            raise ValueError(\n                \"decoder_model_output_type must be 'x0' or 'v', got \"\n                f\"{arch.decoder_model_output_type!r}.\"\n            )\n        # An inconsistent pair would only fail deep inside the first block.\n        for stage_idx, reduction in enumerate(reductions):\n            expected = stage_channels[stage_idx] // reduction\n            if stage_channels[stage_idx + 1] != expected:\n                raise ValueError(\n                    f\"decoder_stage_channels[{stage_idx + 1}] must be \"\n                    f\"{expected}, got {stage_channels[stage_idx + 1]}.\"\n                )\n\n        self.patch_size = arch.patch_size\n        self.out_channels = arch.out_channels\n        self.timestep_scale_multiplier = arch.decoder_timestep_scale_multiplier\n        self.model_output_type = arch.decoder_model_output_type\n        self.default_num_inference_steps = arch.decoder_num_inference_steps\n        self.temporal_compression_ratio = arch.temporal_compression_ratio\n        self.context_channels = stage_channels[-1]\n        # Replicated through stages 1-4 and cropped before stage 5, moving the\n        # border effect past the frames that are kept.\n        self.trailing_pad_latent_frames = (stage_kernels[0][0] // 2) * 2\n\n        self.conv_in = nn.Linear(arch.latent_channels, stage_channels[0], bias=True)\n\n        self.det_stages = nn.ModuleList()","sourceCodeStart":643,"sourceCodeEnd":679,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/multimodal_gen/runtime/models/decoders/ltx_2_5_diffusion_decoder.py#L643-L679","documentation":"The decoder's upsampling stages reduce channels by decoder_upsample_channel_reductions between consecutive stages; stage N+1's channel count must equal stage N's channels divided by that stage's reduction factor. __init__ cross-checks the decoder_stage_channels list against the reductions and raises on any inconsistent pair, catching config errors early instead of inside the first block forward.","triggerScenarios":"Building the decoder with a decoder_stage_channels list where any consecutive pair is not exactly related by the corresponding decoder_upsample_channel_reductions factor — e.g. [640, 320, 160] with reductions [2, 4] (320 != 640//2 is fine, but 160 != 320//4 fails).","commonSituations":"Editing stage widths for a smaller model variant without recomputing reductions; merging configs from different model revisions; typos in the channel or reduction lists.","solutions":["Recompute the chain: stage_channels[i+1] must equal stage_channels[i] // reductions[i] for every stage; fix the offending entry indicated by stage_idx+1","Prefer deriving channel lists from a base width and the reduction factors programmatically instead of hand-writing both lists","Diff your arch config against the shipped LTX-2.5 defaults to spot the edited entry"],"exampleFix":"# before\narch.decoder_stage_channels = (640, 320, 160)\narch.decoder_upsample_channel_reductions = (2, 4)  # 320//4=80 != 160 -> error\n# after\narch.decoder_stage_channels = (640, 320, 80)\narch.decoder_upsample_channel_reductions = (2, 4)","handlingStrategy":"validation","validationCode":"ch = list(arch.decoder_stage_channels)\nfor i, r in enumerate(arch.decoder_upsample_channel_reductions):\n    if ch[i+1] != ch[i] // r:\n        raise ValueError(f\"stage {i+1} channels {ch[i+1]} != {ch[i]}//{r}\")","typeGuard":"def stages_consistent(channels: tuple[int,...], reductions: tuple[int,...]) -> bool:\n    return len(channels) == len(reductions) + 1 and all(\n        channels[i+1] == channels[i] // reductions[i] for i in range(len(reductions))\n    )","tryCatchPattern":"try:\n    decoder = Ltx25DiffusionDecoder(arch)\nexcept ValueError as e:\n    if \"decoder_stage_channels\" in str(e):\n        arch.decoder_stage_channels = derive_from_base(base, reductions)\n        decoder = Ltx25DiffusionDecoder(arch)\n    else:\n        raise","preventionTips":["Derive stage channels programmatically from a base width and reduction factors instead of hand-editing lists","Validate the full arch in a config sanity check before model construction"],"tags":["model-config","channel-dimensions","config-validation","ltx-2"],"backgroundTag":"inconsistent-model-config","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}