invoke-ai/InvokeAI · error · ValueError

out_channels must be divisible by groups

Error message

out_channels must be divisible by groups

What it means

Same Conv2d wrapper in PidiNet's model.py also requires out_channels to be an integer multiple of groups, mirroring nn.Conv2d semantics. ValueError('out_channels must be divisible by groups') is raised in __init__ when out_channels % groups != 0, since each group must receive an equal share of output channels.

Source

Thrown at invokeai/backend/image_util/pidi/model.py:355

            weights = weights.view(shape[0], shape[1], -1)
            buffer[:, :, [0, 2, 4, 10, 14, 20, 22, 24]] = weights[:, :, 1:]
            buffer[:, :, [6, 7, 8, 11, 13, 16, 17, 18]] = -weights[:, :, 1:]
            buffer[:, :, 12] = 0
            buffer = buffer.view(shape[0], shape[1], 5, 5)
            y = F.conv2d(x, buffer, bias, stride=stride, padding=padding, dilation=dilation, groups=groups)
            return y
        return func
    else:
        print('impossible to be here unless you force that')
        return None

class Conv2d(nn.Module):
    def __init__(self, pdc, in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=False):
        super(Conv2d, self).__init__()
        if in_channels % groups != 0:
            raise ValueError('in_channels must be divisible by groups')
        if out_channels % groups != 0:
            raise ValueError('out_channels must be divisible by groups')
        self.in_channels = in_channels
        self.out_channels = out_channels
        self.kernel_size = kernel_size
        self.stride = stride
        self.padding = padding
        self.dilation = dilation
        self.groups = groups
        self.weight = nn.Parameter(torch.Tensor(out_channels, in_channels // groups, kernel_size, kernel_size))
        if bias:
            self.bias = nn.Parameter(torch.Tensor(out_channels))
        else:
            self.register_parameter('bias', None)
        self.reset_parameters()
        self.pdc = pdc

    def reset_parameters(self):
        nn.init.kaiming_uniform_(self.weight, a=math.sqrt(5))
        if self.bias is not None:

View on GitHub (pinned to 0b6a024f2f)

Solutions

  1. Set groups=1 unless grouped convolutions are required.
  2. Round out_channels up to the nearest multiple of groups (e.g. ceil_div(out_channels, groups) * groups).
  3. Reduce groups to a divisor of out_channels.
  4. If both grouped constraints are hard, pick groups = gcd(in_channels, out_channels).

Example fix

// before
conv = Conv2d(pdc, in_channels=32, out_channels=10, kernel_size=3, groups=4)  # 10 % 4 != 0
// after
conv = Conv2d(pdc, in_channels=32, out_channels=12, kernel_size=3, groups=4)
Defensive patterns

Strategy: validation

Validate before calling

if groups > 1 and out_channels % groups != 0:
    out_channels = ((out_channels + groups - 1) // groups) * groups  # round up to multiple

Try / catch

try:
    conv = Conv2d(pdc, in_channels, out_channels, kernel_size, groups=groups)
except ValueError as e:
    if 'divisible by groups' in str(e):
        groups = 1
        conv = Conv2d(pdc, in_channels, out_channels, kernel_size, groups=groups)
    else:
        raise

Prevention

When it happens

Trigger: Constructing Conv2d(pdc, in_channels, out_channels, kernel_size, ..., groups=g) where out_channels % g != 0 — e.g. out_channels=10 with groups=2 (10 % 2 == 0 is fine, but out_channels=10 with groups=4 fails), typically after hand-editing channel widths.

Common situations: Changing a layer's output width for a custom head while leaving grouped configuration intact; copying grouped-conv settings from another architecture; math errors when deriving group counts from channel counts.

Related errors


AI-assisted analysis of invoke-ai/InvokeAI@0b6a024f2f (2026-08-29). Data as JSON: /api/errors/20a3d8d9cfff04d4. Report an issue: GitHub.