sgl-project/sglang · error · ValueError

decoder_stage_channels[{stage_idx + 1}] must be {expected},

Error message

decoder_stage_channels[{stage_idx + 1}] must be {expected}, got {stage_channels[stage_idx + 1]}.

What it means

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.

Source

Thrown at python/sglang/multimodal_gen/runtime/models/decoders/ltx_2_5_diffusion_decoder.py:661

    def __init__(self, config: LTX25DiffusionDecoderConfig) -> None:
        super().__init__()
        arch = config.arch_config
        stage_channels = tuple(arch.decoder_stage_channels)
        stage_depths = tuple(arch.decoder_stage_depths)
        stage_kernels = tuple(tuple(k) for k in arch.decoder_stage_kernels)
        upsample_strides = tuple(tuple(s) for s in arch.decoder_upsample_strides)
        reductions = tuple(arch.decoder_upsample_channel_reductions)

        if arch.decoder_model_output_type not in ("x0", "v"):
            raise ValueError(
                "decoder_model_output_type must be 'x0' or 'v', got "
                f"{arch.decoder_model_output_type!r}."
            )
        # An inconsistent pair would only fail deep inside the first block.
        for stage_idx, reduction in enumerate(reductions):
            expected = stage_channels[stage_idx] // reduction
            if stage_channels[stage_idx + 1] != expected:
                raise ValueError(
                    f"decoder_stage_channels[{stage_idx + 1}] must be "
                    f"{expected}, got {stage_channels[stage_idx + 1]}."
                )

        self.patch_size = arch.patch_size
        self.out_channels = arch.out_channels
        self.timestep_scale_multiplier = arch.decoder_timestep_scale_multiplier
        self.model_output_type = arch.decoder_model_output_type
        self.default_num_inference_steps = arch.decoder_num_inference_steps
        self.temporal_compression_ratio = arch.temporal_compression_ratio
        self.context_channels = stage_channels[-1]
        # Replicated through stages 1-4 and cropped before stage 5, moving the
        # border effect past the frames that are kept.
        self.trailing_pad_latent_frames = (stage_kernels[0][0] // 2) * 2

        self.conv_in = nn.Linear(arch.latent_channels, stage_channels[0], bias=True)

        self.det_stages = nn.ModuleList()

View on GitHub (pinned to 0132848349)

Solutions

  1. 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
  2. Prefer deriving channel lists from a base width and the reduction factors programmatically instead of hand-writing both lists
  3. Diff your arch config against the shipped LTX-2.5 defaults to spot the edited entry

Example fix

# before
arch.decoder_stage_channels = (640, 320, 160)
arch.decoder_upsample_channel_reductions = (2, 4)  # 320//4=80 != 160 -> error
# after
arch.decoder_stage_channels = (640, 320, 80)
arch.decoder_upsample_channel_reductions = (2, 4)
Defensive patterns

Strategy: validation

Validate before calling

ch = list(arch.decoder_stage_channels)
for i, r in enumerate(arch.decoder_upsample_channel_reductions):
    if ch[i+1] != ch[i] // r:
        raise ValueError(f"stage {i+1} channels {ch[i+1]} != {ch[i]}//{r}")

Type guard

def stages_consistent(channels: tuple[int,...], reductions: tuple[int,...]) -> bool:
    return len(channels) == len(reductions) + 1 and all(
        channels[i+1] == channels[i] // reductions[i] for i in range(len(reductions))
    )

Try / catch

try:
    decoder = Ltx25DiffusionDecoder(arch)
except ValueError as e:
    if "decoder_stage_channels" in str(e):
        arch.decoder_stage_channels = derive_from_base(base, reductions)
        decoder = Ltx25DiffusionDecoder(arch)
    else:
        raise

Prevention

When it happens

Trigger: 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).

Common situations: Editing stage widths for a smaller model variant without recomputing reductions; merging configs from different model revisions; typos in the channel or reduction lists.

Related errors


AI-assisted analysis of sgl-project/sglang@0132848349 (2026-08-28). Data as JSON: /api/errors/896cc4fb8a352ca4. Report an issue: GitHub.