huggingface/candle · error
softmax-last-dim is not implemented for {dtype:?}
Error message
softmax-last-dim is not implemented for {dtype:?} What it means
The Metal softmax kernel dispatches to named metal shaders softmax_f32/softmax_f16/softmax_bf16 only. Any other dtype (F64, integers, etc.) has no GPU kernel registered, so metal_fwd bails with this message naming the dtype.
Source
Thrown at candle-nn/src/ops.rs:404
Ok((dst, layout.shape().clone()))
}
#[cfg(feature = "metal")]
fn metal_fwd(
&self,
storage: &candle::MetalStorage,
layout: &Layout,
) -> Result<(candle::MetalStorage, Shape)> {
use candle::backend::BackendStorage;
let device = storage.device();
let encoder = device.command_encoder()?;
encoder.set_label("softmax");
let kernels = device.kernels();
let name = match storage.dtype() {
DType::F32 => "softmax_f32",
DType::F16 => "softmax_f16",
DType::BF16 => "softmax_bf16",
dtype => candle::bail!("softmax-last-dim is not implemented for {dtype:?}"),
};
let n = layout.stride().len();
if !(layout.is_contiguous() && layout.stride()[n - 1] == 1) {
candle::bail!("Non contiguous softmax-last-dim is not implemented");
}
let last_dim = layout.dims()[layout.shape().rank() - 1];
let elem_count = layout.shape().elem_count();
let output = device
.new_buffer_builder()
.with_size_for(elem_count, storage.dtype())
.with_label("softmax")
.build()?;
candle_metal_kernels::call_last_softmax(
device.metal_device(),
&encoder,
kernels,View on GitHub (pinned to d5fee525bf)
Solutions
- Cast to F32 (or F16/BF16) with .to_dtype() before the softmax
- Keep model tensors in F32/F16/BF16 on Metal; avoid F64 on GPU backends
- Verify the op's device matches the intended backend; CPU supports F64 softmax but Metal does not
Example fix
// before let probs = softmax_last_dim(&logits.to_device(&metal_device)?)?; // F64 // after let probs = softmax_last_dim(&logits.to_dtype(DType::F32)?.to_device(&metal_device)?)?;
Defensive patterns
Strategy: validation
Validate before calling
fn ensure_metal_softmax(t: &Tensor) -> Result<()> {
if !matches!(t.dtype(), DType::F32 | DType::F16 | DType::BF16) {
bail!("Metal softmax supports F32/F16/BF16 only, got {:?}", t.dtype());
}
Ok(())
} Type guard
fn metal_softmax_ok(d: DType) -> bool { matches!(d, DType::F32 | DType::F16 | DType::BF16) } Try / catch
let t = if metal_softmax_ok(t.dtype()) { t } else { t.to_dtype(DType::F32)? }; Prevention
- Never use F64 tensors on Metal devices
- Keep GPU pipeline dtypes uniform (F32 or F16/BF16)
- Cast at device-transfer time: to_dtype + to_device together
When it happens
Trigger: Calling ops::softmax_last_dim on a Metal-device tensor with a dtype other than F32/F16/BF16, such as F64 or I64.
Common situations: Default F64 tensors created from Rust f64 arrays and sent to a Metal device; index/integer tensors accidentally passed to softmax; dtype changed upstream by a model config.
Related errors
- Metal contiguous unary {name} {dtype:?} not implemented
- Metal strided unary {name} {dtype:?} not implemented
- Metal where_cond {left:?} {right:?} not implemented
- Metal conv1d {dtype:?} not implemented
- metal col2im1d {dtype:?} not implemented
AI-assisted analysis of huggingface/candle@d5fee525bf (2026-09-02).
Data as JSON: /api/errors/8514524bf257c7d3.
Report an issue: GitHub.