{"record":{"id":"972f6c0ee9ea8896","repo":"tracel-ai/burn","slug":"channels-must-be-divisible-by-the-number-of-groups","errorCode":null,"errorMessage":"Channels must be divisible by the number of groups. Got channels_in={}, offset_groups={}","messagePattern":"Channels must be divisible by the number of groups\\. Got channels_in=(.+?), offset_groups=(.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/burn-nn/src/modules/conv/deform_conv2d.rs","lineNumber":85,"sourceCode":"    pub dilation: [usize; 2],\n    /// Controls the connections between input and output channels.\n    pub weight_groups: usize,\n    /// Offset groups.\n    pub offset_groups: usize,\n    /// The padding configuration.\n    #[module(skip)]\n    pub padding: PaddingConfig2d,\n}\n\nimpl DeformConv2dConfig {\n    /// Initialize a new [DeformConv2d](DeformConv2d) module.\n    pub fn init(&self, device: &Device) -> DeformConv2d {\n        checks::checks_channels_div_groups(self.channels[0], self.channels[1], self.weight_groups);\n        if self.padding == PaddingConfig2d::Same {\n            checks::check_same_padding_support(&self.kernel_size);\n        }\n        if !self.channels[0].is_multiple_of(self.offset_groups) {\n            panic!(\n                \"Channels must be divisible by the number of groups. Got \\\n                 channels_in={}, offset_groups={}\",\n                self.channels[0], self.offset_groups\n            );\n        }\n\n        let shape = [\n            self.channels[1],\n            self.channels[0] / self.weight_groups,\n            self.kernel_size[0],\n            self.kernel_size[1],\n        ];\n\n        let k = self.kernel_size.iter().product::<usize>();\n        let fan_in = self.channels[0] / self.weight_groups * k;\n        let fan_out = self.channels[1] / self.weight_groups * k;\n\n        let weight = self","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/tracel-ai/burn/blob/d16f7ba2ed0d41408189384044cc886fb4c8f957/crates/burn-nn/src/modules/conv/deform_conv2d.rs#L67-L103","documentation":"DeformConv2d requires the input channel count to be divisible by `offset_groups`, because offsets are predicted per group of input channels. In `DeformConv2dConfig::init`, after the generic grouped-conv check, a separate check panics when `channels[0] % offset_groups != 0`.","triggerScenarios":"Building a `DeformConv2d` via `DeformConv2dConfig::init(&device)` where `channels[0]` (in channels) is not a multiple of `offset_groups`, e.g. channels=[3, 64] with offset_groups=8.","commonSituations":"Copying a config that used offset_groups=8 with 256-channel inputs onto an RGB 3-channel input; tuning offset_groups without updating channels; default configs mismatched with a new first layer.","solutions":["Set `offset_groups` to a divisor of channels_in (common values: 1, 4, 8 with channels_in a multiple of them).","Change the input channel count (e.g. via an initial conv) so it is divisible by offset_groups.","Set offset_groups = 1 to use a single offset group.","Also ensure channels_in/channels_out are divisible by `weight_groups` (separate check that runs first)."],"exampleFix":"// before\nlet config = DeformConv2dConfig { channels: [3, 64], offset_groups: 8, ..Default::default() }; // 3 % 8 != 0\n// after\nlet config = DeformConv2dConfig { channels: [3, 64], offset_groups: 1, ..Default::default() };","handlingStrategy":"validation","validationCode":"fn validate_deform_conv(config: &DeformConv2dConfig) {\n    assert!(config.channels[0].is_multiple_of(config.offset_groups),\n        \"channels_in={} not divisible by offset_groups={}\", config.channels[0], config.offset_groups);\n    assert!(config.channels[0].is_multiple_of(config.weight_groups));\n    assert!(config.channels[1].is_multiple_of(config.weight_groups));\n}\nvalidate_deform_conv(&config);","typeGuard":"fn deform_groups_valid(channels_in: usize, channels_out: usize, offset_groups: usize, weight_groups: usize) -> bool {\n    channels_in.is_multiple_of(offset_groups)\n        && channels_in.is_multiple_of(weight_groups)\n        && channels_out.is_multiple_of(weight_groups)\n}","tryCatchPattern":"let result = std::panic::catch_unwind(|| config.init(&device));\nmatch result {\n    Ok(layer) => layer,\n    Err(_) => {\n        let mut c = config;\n        c.offset_groups = 1;\n        c.init(&device)\n    }\n}","preventionTips":["Keep offset_groups at 1 unless channels_in is known to be a multiple (e.g. 8 | 256).","Assert divisibility when loading configs from checkpoints/JSON.","Remember two independent checks: offset_groups vs channels_in, and weight_groups vs both channels."],"tags":["rust","panic","convolution","config-validation","shape-mismatch"],"backgroundTag":"channels-not-divisible-by-groups","analyzedSha":"d16f7ba2ed0d41408189384044cc886fb4c8f957","analyzedAt":"2026-09-05T13:19:14.260Z","contentChangedAt":"2026-09-05T13:19:14.260Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}