huggingface/candle · error · candle::Error

unexpected num-channels in GroupNorm ({n_channels} <> {}

Error message

unexpected num-channels in GroupNorm ({n_channels} <> {}

What it means

During forward, GroupNorm checks that the input's channel dimension (dims[1]) matches the num_channels the layer was constructed with. This bail fires when they differ. Note the message itself has a malformed format string (a stray '{}') but the data reported is the actual channel count vs the configured one.

Source

Thrown at candle-nn/src/group_norm.rs:48

            weight,
            bias,
            eps,
            num_channels,
            num_groups,
        })
    }
}

impl crate::Module for GroupNorm {
    fn forward(&self, x: &Tensor) -> Result<Tensor> {
        let x_shape = x.dims();
        if x_shape.len() <= 2 {
            candle::bail!("input rank for GroupNorm should be at least 3");
        }
        let (b_sz, n_channels) = (x_shape[0], x_shape[1]);
        let hidden_size = x_shape[2..].iter().product::<usize>() * n_channels / self.num_groups;
        if n_channels != self.num_channels {
            candle::bail!(
                "unexpected num-channels in GroupNorm ({n_channels} <> {}",
                self.num_channels
            )
        }
        let x_dtype = x.dtype();
        let internal_dtype = match x_dtype {
            DType::F16 | DType::BF16 => DType::F32,
            d => d,
        };
        let x = x.reshape((b_sz, self.num_groups, hidden_size))?;
        let x = x.to_dtype(internal_dtype)?;
        let mean_x = (x.sum_keepdim(2)? / hidden_size as f64)?;
        let x = x.broadcast_sub(&mean_x)?;
        let norm_x = (x.sqr()?.sum_keepdim(2)? / hidden_size as f64)?;
        let x_normed = x.broadcast_div(&(norm_x + self.eps)?.sqrt()?)?;
        let mut w_dims = vec![1; x_shape.len()];
        w_dims[1] = n_channels;
        let weight = self.weight.reshape(w_dims.clone())?;

View on GitHub (pinned to d5fee525bf)

Solutions

  1. Construct GroupNorm with num_channels equal to the input's dim 1 — derive it from the preceding layer's out_channels.
  2. If loading weights, verify the GroupNorm weight shape (weight.dims()[0]) matches the model's channel count for that layer.
  3. Check the tensor order: a transposed tensor (channels last) will present the wrong dim at index 1; permute before forward.
  4. Fix the layer wiring if a GroupNorm instance is shared between two branches with different channel widths; create one per width.

Example fix

// before
let norm = GroupNorm::new(w, b, 64, 32, 1e-5)?;
let y = norm.forward(&conv_out)?; // conv_out dims [B, 128, H, W]
// after
let c = conv_out.dims()[1];
let norm = GroupNorm::new(w, b, c, 32, 1e-5)?;
let y = norm.forward(&conv_out)?;
Defensive patterns

Strategy: validation

Validate before calling

let c_in = x.dims()[1];
if c_in != norm.num_channels() {
    return Err(anyhow!("input has {c_in} channels, GroupNorm built for {}", norm.num_channels()));
}

Type guard

fn channels_match(norm: &candle_nn::GroupNorm, x: &Tensor) -> bool {
    x.rank() >= 2 && x.dims()[1] == norm.num_channels()
}

Try / catch

let y = match norm.forward(&x) {
    Ok(y) => y,
    Err(e) if e.to_string().contains("num-channels") => {
        let c = x.dims()[1];
        let (w, b) = rebuild_norm_weights(c)?; // or permute x if channels-last
        GroupNorm::new(w, b, c, 32, 1e-5)?.forward(&x)?
    }
    Err(e) => return Err(e.into()),
};

Prevention

When it happens

Trigger: Calling forward on a GroupNorm built for N channels with an input tensor whose second dimension is a different channel count — e.g. GroupNorm configured for 64 channels receiving a tensor with 128 channels.

Common situations: Reusing a norm layer across architecture stages with different widths; loading a checkpoint whose channel count differs from the freshly built model; model config edited (width multiplier) without updating the norm layers; wrong tensor passed to forward.

Related errors


AI-assisted analysis of huggingface/candle@d5fee525bf (2026-09-02). Data as JSON: /api/errors/20e3c2ce96473b08. Report an issue: GitHub.