tracel-ai/burn · info

grid_sample_2d: NumCast::from({x:?}) to {:?} returned None

Error message

grid_sample_2d: NumCast::from({x:?}) to {:?} returned None

What it means

Inside grid_sample_2d_impl, from_f64 converts computed coordinates back to the element type T via NumCast::from, which returns None when the f64 value is not representable in T. The panic is a diagnostic hatch: for current float dtypes ToElement::to_f64 preserves non-finite values so from() always succeeds; if a future dtype breaks that invariant, this panic surfaces it.

Source

Thrown at crates/burn-flex/src/ops/grid_sample.rs:105

    let g_stride_h = w_out * 2;

    let o_stride_n = channels * h_out * w_out;
    let o_stride_c = h_out * w_out;
    let o_stride_h = w_out;

    // Low-precision types (f16/bf16) are widened to f64 for all arithmetic so
    // that coordinate math, weights, and accumulated samples keep full precision.
    //
    // The from_f64 unwrap is unreachable for any well-formed input: bilinear
    // is a convex combination of finite samples so the result stays bounded by
    // the sample envelope. The `half` crate's `NumCast` impl for f16/bf16
    // forwards through `to_f32` and maps non-finite inputs to `Some(inf/nan)`;
    // `num_traits`'s f32/f64 impls do the same. The message-bearing panic is a
    // diagnostic hatch for future dtypes where this invariant does not hold.
    let to_f64 = |x: T| -> f64 { ToElement::to_f64(&x) };
    let from_f64 = |x: f64| -> T {
        <T as NumCast>::from(x).unwrap_or_else(|| {
            panic!(
                "grid_sample_2d: NumCast::from({x:?}) to {:?} returned None",
                T::dtype()
            )
        })
    };

    for b in 0..batch_size {
        for y in 0..h_out {
            for x in 0..w_out {
                let g_idx = b * g_stride_n + y * g_stride_h + x * 2;
                let sample_x = to_f64(grid_data[g_idx]);
                let sample_y = to_f64(grid_data[g_idx + 1]);

                let (px, py) = if align {
                    let px = (sample_x + 1.0) * ((w_in - 1) as f64) / 2.0;
                    let py = (sample_y + 1.0) * ((h_in - 1) as f64) / 2.0;
                    (px, py)
                } else {

View on GitHub (pinned to d16f7ba2ed)

Solutions

  1. Nothing to fix for stock float dtypes — this path cannot return None there
  2. If you hit it with a custom dtype, ensure its NumCast impl and to_f64 conversion are lossless for non-finite values
  3. Clamp grid coordinates to finite values before grid_sample when feeding unusual data
  4. Report it to burn-flex maintainers with the dtype and input values if reachable
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure grid coordinates are finite before grid_sample_2d
assert!(grid.to_data().value.iter().all(|v| v.is_finite() || v.is_nan()), "non-finite handling differs per dtype");

Try / catch

// this panic is not catchable in Rust without catch_unwind
let result = std::panic::catch_unwind(AssertUnwindSafe(||
    backend.grid_sample_2d(tensor.clone(), grid.clone(), options.clone())
));

Prevention

When it happens

Trigger: Effectively unreachable with F32/F64/F16/BF16 today; would trigger if a non-standard element type T were used where to_f64 loses representability (e.g. an integer T given a fractional/overflowing f64 coordinate).

Common situations: Custom element types added to the backend, or extreme NaN/inf handling differences in a future dtype; as a library user on stock float dtypes you should not hit this.

Related errors


AI-assisted analysis of tracel-ai/burn@d16f7ba2ed (2026-09-05). Data as JSON: /api/errors/94bbf3d15d2875b6. Report an issue: GitHub.