huggingface/candle · error
backward not supported for upsample_bilinear2d
Error message
backward not supported for upsample_bilinear2d
What it means
candle has no backward implementation for the UpsampleBilinear2D op at all; when backward_step walks the op graph and reaches it, it unconditionally bails. Unlike nearest-neighbor upsampling (whose gradient is a strided sum), bilinear interpolation gradients were simply not implemented.
Source
Thrown at candle-core/src/backprop.rs:412
} => {
let (_n, c, h, w) = arg.dims4()?;
if target_h % h != 0 || target_w % w != 0 {
crate::bail!("backward not supported for non integer upscaling factors")
}
let scale_h = target_h / h;
let scale_w = target_w / w;
if scale_h != scale_w {
crate::bail!("backward not supported for non uniform upscaling factors")
};
let kernel =
Tensor::ones((c, 1, scale_h, scale_w), arg.dtype(), arg.device())?;
let conv_sum = grad.conv2d(&kernel, 0, scale_h, 1, c)?;
let sum_grad = grads.or_insert(arg)?;
*sum_grad = conv_sum;
}
Op::UpsampleBilinear2D { .. } => {
crate::bail!("backward not supported for upsample_bilinear2d")
}
Op::SliceScatter0(lhs, rhs, start_rhs) => {
let rhs_sum_grad = grads.or_insert(rhs)?;
let rhs_grad = grad.narrow(0, *start_rhs, rhs.dim(0)?)?;
*rhs_sum_grad = rhs_sum_grad.add(&rhs_grad)?;
let lhs_sum_grad = grads.or_insert(lhs)?;
let lhs_grad = grad.slice_scatter0(&rhs.zeros_like()?, *start_rhs)?;
*lhs_sum_grad = lhs_sum_grad.add(&lhs_grad)?
}
Op::Gather(arg, indexes, dim) => {
let sum_grad = grads.or_insert(arg)?;
*sum_grad = sum_grad.scatter_add(indexes, &grad, *dim)?;
}
Op::Scatter(init, indexes, src, dim) => {
let init_sum_grad = grads.or_insert(init)?;
*init_sum_grad = init_sum_grad.add(&grad)?;
View on GitHub (pinned to d5fee525bf)
Solutions
- Replace upsample_bilinear2d with upsample_nearest2d (backward supported for integer uniform scales).
- Replace the bilinear upsample with a ConvTranspose2d layer (learned upsampling), which is differentiable in candle.
- Stop the gradient at the upsample by calling detach() on the tensor before the op (training downstream layers only).
- Implement the op with primitive ops (e.g. gather + weighted combinations) so autograd can differentiate through it.
Example fix
// before let up = x.upsample_bilinear2d(64, 64)?; // after let up = x.upsample_nearest2d(64, 64)?; // integer, uniform -> backward works
Defensive patterns
Strategy: fallback
Validate before calling
// detect bilinear upsample in graph inputs before building the VarMap training loop
let uses_bilinear = layers.iter().any(|l| l.kind == LayerKind::UpsampleBilinear2D);
if uses_bilinear { eprintln!("warning: bilinear upsample has no backward in candle; swapping to nearest"); } Try / catch
match loss.backward() { Err(e) if e.to_string().contains("upsample_bilinear2d") => { // rebuild model with upsample_nearest2d or ConvTranspose2d
}, other => other?, } Prevention
- Never call upsample_bilinear2d inside trainable forward passes; keep it inference-only.
- Prefer upsample_nearest2d or ConvTranspose2d when gradients are needed.
- Search imported model code (PyTorch ports) for interpolate(mode='bilinear') and replace before training.
When it happens
Trigger: Any call to Tensor::upsample_bilinear2d(...) (directly or inside a model forward) followed by backward() on a loss that depends on it.
Common situations: Porting PyTorch models using F.interpolate(mode='bilinear') or nn.Upsample(mode='bilinear'); training U-Net-style architectures with bilinear upsampling in candle.
Related errors
- backward not supported for non uniform upscaling factors
- in_channel mismatch between input ({c_in}) and kernel ({c_in
- dtype mismatch
- one_hot: index value {value} exceeds depth {depth}
- one_hot: index out of bounds {idx}, len {}
AI-assisted analysis of huggingface/candle@d5fee525bf (2026-09-02).
Data as JSON: /api/errors/e3333e0452c7ecd5.
Report an issue: GitHub.