huggingface/candle · error

metal col2im1d not implemented

Error message

metal col2im1d {dtype:?} not implemented

What it means

In the col2im-based conv_transpose1d path, Metal dispatches a col2im1d kernel by dtype; only F32, F16, BF16, U32, U8 have kernels. Any other dtype bails here, meaning transposed convolution cannot run for that dtype via this path.

Solutions

  1. Cast input and kernel to F32/F16/BF16 before conv_transpose1d
  2. Cast back to the original dtype after the op if needed
  3. Run conv_transpose1d on CPU for that tensor

Example fix

// before
let y = x_i64.conv_transpose1d(&k, padding, output_padding, stride, dilation, groups)?;
// after
let y = x_i64.to_dtype(DType::F32)?.conv_transpose1d(&k.to_dtype(DType::F32)?, padding, output_padding, stride, dilation, groups)?;
Defensive patterns

Strategy: fallback

Validate before calling

if !matches!(x.dtype(), DType::F32 | DType::F16 | DType::BF16 | DType::U32 | DType::U8) {
    x = x.to_dtype(DType::F32)?;
    kernel = kernel.to_dtype(DType::F32)?;
}

Try / catch

match x.conv_transpose1d(&k, p, op, s, d, g) {
    Ok(y) => y,
    Err(e) if e.to_string().contains("col2im1d") => {
        x.to_dtype(DType::F32)?.conv_transpose1d(&k.to_dtype(DType::F32)?, p, op, s, d, g)
    }
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: Calling conv_transpose1d on Metal (with USE_COL2IM_CONV1D_TR enabled and col2im applicable) on an I64 or otherwise unsupported dtype tensor.

Common situations: Integer activations flowing into a transposed conv; dtype mismatch from a quantization pipeline; models ported with weights in i64.

Related errors


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

Appendix: source

Thrown at candle-core/src/metal_backend/mod.rs:1025

                    "convtr1d: shape mismatch on c_in {:?} {:?}",
                    layout.shape(),
                    k_layout.shape()
                )
            }
            let buffer = self
                .device
                .new_buffer_builder()
                .with_size_for(dst_el, self.dtype)
                .with_label("conv_transpose1d")
                .build()?;

            let name = match self.dtype {
                DType::F32 => "col2im1d_f32",
                DType::F16 => "col2im1d_f16",
                DType::BF16 => "col2im1d_bf16",
                DType::U32 => "col2im1d_u32",
                DType::U8 => "col2im1d_u8",
                dtype => crate::bail!("metal col2im1d {dtype:?} not implemented"),
            };
            let col = {
                // This merges the last two dimensions of the kernel together.
                let kernel_l_mm = Layout::new(
                    (b_size, c_in, k_size * c_out).into(),
                    vec![0, k_size * c_out, 1],
                    k_layout.start_offset(),
                );
                self.matmul(
                    k,
                    (b_size, l_in, c_out * k_size, c_in),
                    &layout.transpose(1, 2)?,
                    &kernel_l_mm,
                )?
            };
            // It is important for the command encoder to be obtained *after* the matmul
            // kernel has run, otherwise we might use a command-buffer that has been committed
            // already resulting in the following error.

View on GitHub (pinned to d5fee525bf)