huggingface/candle · error

Metal contiguous unary {name} {dtype:?} not implemented

Error message

Metal contiguous unary {name} {dtype:?} not implemented

What it means

Metal contiguous unary ops (neg, exp, tanh, sign, etc.) are dispatched via a (op-name, dtype) kernel table. Requesting a unary op for a dtype without a matching Metal kernel hits this bail. Coverage is limited mostly to F32/F16/BF16 (and a few integer cases like sign on I64).

Source

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

                ("usilu", DType::BF16) => contiguous::silu::BFLOAT,
                ("usin", DType::F16) => contiguous::sin::HALF,
                ("usin", DType::F32) => contiguous::sin::FLOAT,
                ("usin", DType::BF16) => contiguous::sin::BFLOAT,
                ("usqr", DType::F16) => contiguous::sqr::HALF,
                ("usqr", DType::F32) => contiguous::sqr::FLOAT,
                ("usqr", DType::BF16) => contiguous::sqr::BFLOAT,
                ("usqrt", DType::F16) => contiguous::sqrt::HALF,
                ("usqrt", DType::F32) => contiguous::sqrt::FLOAT,
                ("usqrt", DType::BF16) => contiguous::sqrt::BFLOAT,
                ("utanh", DType::F16) => contiguous::tanh::HALF,
                ("utanh", DType::F32) => contiguous::tanh::FLOAT,
                ("utanh", DType::BF16) => contiguous::tanh::BFLOAT,
                ("usign", DType::F16) => contiguous::sign::HALF,
                ("usign", DType::F32) => contiguous::sign::FLOAT,
                ("usign", DType::BF16) => contiguous::sign::BFLOAT,
                ("usign", DType::I64) => contiguous::sign::I64,
                (name, dtype) => {
                    crate::bail!("Metal contiguous unary {name} {dtype:?} not implemented")
                }
            };

            candle_metal_kernels::call_unary_contiguous(
                &device.device,
                &encoder,
                &device.kernels,
                kernel_name,
                dtype.size_in_bytes(),
                el_count,
                src,
                &buffer,
            )
            .map_err(MetalError::from)?;
        } else {
            use candle_metal_kernels::unary::strided;
            let kernel_name = match (B::KERNEL, dtype) {
                ("ucos", DType::F32) => strided::cos::FLOAT,

View on GitHub (pinned to d5fee525bf)

Solutions

  1. Cast the tensor to F32 (or F16/BF16) before the unary op, cast back afterwards
  2. Perform the unary op on CPU for that tensor
  3. Use a dtype supported for that specific op on Metal
  4. Check candle_metal_kernels unary coverage and upgrade candle for newer kernels

Example fix

// before
let y = x_i64.exp()?; // fails on Metal
// after
let y = x_i64.to_dtype(DType::F32)?.exp()?.to_dtype(DType::I64)?;
Defensive patterns

Strategy: fallback

Validate before calling

const METAL_UNARY_FLOAT_ONLY: [&str; 2] = ["exp", "log"]; // ops without integer kernels
let needs_float = METAL_UNARY_FLOAT_ONLY.contains(op_name);
if needs_float && !matches!(t.dtype(), DType::F32 | DType::F16 | DType::BF16) {
    t = t.to_dtype(DType::F32)?;
}

Try / catch

match t.unary(op) {
    Ok(y) => y,
    Err(e) if e.to_string().contains("unary") && e.to_string().contains("not implemented") => {
        let orig = t.dtype();
        t.to_dtype(DType::F32)?.unary(op)?.to_dtype(orig)?
    }
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: Applying any unary op (exp, log, sin, sqrt, sign, relu, ...) to a Metal tensor whose dtype lacks a kernel, e.g. u8::sqrt or i64::exp via the contiguous path.

Common situations: Integer tensors on Metal being passed through math ops; fp16 model running an op implemented only for f32; quantized/int8 tensors needing float-only math.

Related errors


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