tracel-ai/burn · error

Both channels must be divisible by the number of groups. Got

Error message

Both channels must be divisible by the number of groups. Got channels_in={channels_in}, channels_out={channels_out}, groups={groups}

What it means

Convolution layer configuration validation requires both input and output channel counts to be evenly divisible by the group count (grouped/depthwise convolution). If either `channels_in % groups != 0` or `channels_out % groups != 0`, the weight tensor cannot be shaped into groups, so `checks_channels_div_groups` panics at layer construction.

Source

Thrown at crates/burn-nn/src/modules/conv/checks.rs:6

pub(crate) fn checks_channels_div_groups(channels_in: usize, channels_out: usize, groups: usize) {
    let channels_in_div_by_group = channels_in.is_multiple_of(groups);
    let channels_out_div_by_group = channels_out.is_multiple_of(groups);

    if !channels_in_div_by_group || !channels_out_div_by_group {
        panic!(
            "Both channels must be divisible by the number of groups. Got \
             channels_in={channels_in}, channels_out={channels_out}, groups={groups}"
        );
    }
}

// https://github.com/tracel-ai/burn/issues/2676
/// Only symmetric padding is currently supported. As such, using `Same` padding with an even kernel
/// size is not supported as it will not produce the same output size.
pub(crate) fn check_same_padding_support(kernel_size: &[usize]) {
    for k in kernel_size.iter() {
        if k % 2 == 0 {
            unimplemented!("Same padding with an even kernel size is not supported");
        }
    }
}

View on GitHub (pinned to d16f7ba2ed)

Solutions

  1. Make `groups` a divisor of both channels_in and channels_out (e.g. groups of 1, 2, 4, ... that divide both).
  2. For depthwise convs, set channels_out = channels_in * multiplier with groups = channels_in.
  3. Set groups = 1 (standard convolution) if grouping is not required.
  4. Assert divisibility in your config-builder before constructing the layer.

Example fix

// before
let config = Conv2dConfig { channels: [3, 16], groups: 2, ..Default::default() }; // 3 % 2 != 0
// after
let config = Conv2dConfig { channels: [4, 16], groups: 2, ..Default::default() }; // or groups: 1
Defensive patterns

Strategy: validation

Validate before calling

fn validate_grouped_conv(channels_in: usize, channels_out: usize, groups: usize) {
    assert!(groups > 0, "groups must be nonzero");
    assert!(channels_in % groups == 0, "channels_in={channels_in} not divisible by groups={groups}");
    assert!(channels_out % groups == 0, "channels_out={channels_out} not divisible by groups={groups}");
}
validate_grouped_conv(config.channels[0], config.channels[1], config.groups);

Type guard

fn groups_are_valid(channels_in: usize, channels_out: usize, groups: usize) -> bool {
    groups > 0 && channels_in.is_multiple_of(groups) && channels_out.is_multiple_of(groups)
}

Try / catch

let result = std::panic::catch_unwind(|| config.init(&device));
match result {
    Ok(layer) => layer,
    Err(_) => {
        let mut c = config;
        c.groups = 1; // safe fallback: standard convolution
        c.init(&device)
    }
}

Prevention

When it happens

Trigger: Constructing any conv config (Conv1d/2d/3d, and deform conv's weight_groups) via `init(&device)` where `groups` does not evenly divide `channels[0]` (in) or `channels[1]` (out), e.g. Conv2dConfig { channels: [3, 16], groups: 2 }.

Common situations: Setting depthwise `groups = channels_in` but forgetting that channels_out must also be divisible (common with padding-out channels); hand-editing configs; switching a standard conv to grouped without adjusting channels.

Related errors


AI-assisted analysis of tracel-ai/burn@d16f7ba2ed (2026-09-05). Data as JSON: /api/errors/b22e1491d83f944e. Report an issue: GitHub.