huggingface/candle · error · candle::Error
GroupNorm: num_groups ({num_groups}) must divide num_channel
Error message
GroupNorm: num_groups ({num_groups}) must divide num_channels ({num_channels}) What it means
GroupNorm::new validates the layer configuration: the channel count must be evenly divisible by the number of groups, because GroupNorm splits channels into groups before normalizing. This bail fires at construction time when num_channels is not a multiple of num_groups.
Source
Thrown at candle-nn/src/group_norm.rs:25
#[derive(Clone, Debug)]
pub struct GroupNorm {
weight: Tensor,
bias: Tensor,
eps: f64,
num_channels: usize,
num_groups: usize,
}
impl GroupNorm {
pub fn new(
weight: Tensor,
bias: Tensor,
num_channels: usize,
num_groups: usize,
eps: f64,
) -> Result<Self> {
if !num_channels.is_multiple_of(num_groups) {
candle::bail!(
"GroupNorm: num_groups ({num_groups}) must divide num_channels ({num_channels})"
)
}
Ok(Self {
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");View on GitHub (pinned to d5fee525bf)
Solutions
- Pick a num_groups that divides num_channels (e.g. for 48 channels use 2, 3, 4, 6, 8, 12, 16, 24 or 48).
- Common convention: num_groups = 32 (as in BigGAN/ADaPT-style nets) only works when channels % 32 == 0; otherwise fall back to fewer groups or LayerNorm (groups == 1).
- Compute groups programmatically: let num_groups = gcd(num_channels, desired_groups) or clamp to largest divisor <= 32.
- If channels come from a checkpoint, print/inspect the weight shape and derive num_channels from weight.dims()[0].
Example fix
// before let norm = GroupNorm::new(w, b, 48, 32, 1e-5)?; // 48 % 32 != 0 // after let num_groups = 48.min(32); let num_groups = (1..=num_groups).rev().find(|g| 48 % g == 0).unwrap(); let norm = GroupNorm::new(w, b, 48, num_groups, 1e-5)?;
Defensive patterns
Strategy: validation
Validate before calling
fn check_group_norm(channels: usize, groups: usize) -> Result<(), String> {
if channels.is_multiple_of(groups) { Ok(()) } else {
Err(format!("channels {channels} not divisible by groups {groups}"))
}
} Type guard
fn valid_group_norm_config(num_channels: usize, num_groups: usize) -> bool {
num_channels.is_multiple_of(num_groups) && num_groups >= 1
} Try / catch
match GroupNorm::new(w.clone(), b.clone(), channels, groups, 1e-5) {
Ok(gn) => gn,
Err(e) => {
let groups = (1..=32).rev().find(|g| channels % g == 0).unwrap_or(1);
GroupNorm::new(w, b, channels, groups, 1e-5)?
}
} Prevention
- Derive num_channels from the preceding conv layer's out_channels, never hard-code
- Pick groups from the divisor list of channels (32 is only valid when channels % 32 == 0)
- Add a builder helper that auto-selects the largest group count <= 32 dividing the channels
- When porting architectures, compare channel/group config against the reference implementation
When it happens
Trigger: Calling candle_nn::GroupNorm::new(weight, bias, num_channels, num_groups, eps) where num_channels % num_groups != 0 (e.g. 48 channels with 5 groups).
Common situations: Copying a config from a paper whose architecture used a different channel count; typo in group count; adapting a pretrained checkpoint whose conv layers have channel counts incompatible with your chosen groups; generic configs where channels come from a previous layer that changed.
Related errors
- one_hot: index value {value} exceeds depth {depth}
- input rank for GroupNorm should be at least 3
- unexpected num-channels in GroupNorm ({n_channels} <> {}
- layer_types length {} does not match num_hidden_layers {}
- only TorchAttn is supported
AI-assisted analysis of huggingface/candle@d5fee525bf (2026-09-02).
Data as JSON: /api/errors/2074ec3d1669de2a.
Report an issue: GitHub.