tracel-ai/burn · critical

Tensor {id:?} was never written: {error}

Error message

Tensor {id:?} was never written: {error}

What it means

`HandleStore::get_handle` panics with 'was never written' when the handle for a tensor id is `Handle::Errored(error)` — the operation that was supposed to produce this tensor failed, and the stored error is surfaced when the tensor is consumed. The primary guard at the top of get_handle (handle.rs:325-329) reports this normally; this second panic at line 351 is a backstop reached only if the `errored` counter has drifted out of sync with the map, i.e. the put/take invariant is already broken.

Source

Thrown at crates/burn-ir/src/handle.rs:351

            .unwrap_or_else(|| panic!("Should have handle for tensor {id:?}"));

        match handle {
            Handle::Existing(handle) => match status {
                TensorStatus::ReadOnly => {
                    self.put(id, Handle::Existing(handle.clone()));
                    handle
                }
                TensorStatus::ReadWrite => handle,
                TensorStatus::NotInit => panic!(
                    "Cannot get uninitialized tensor {id:?}. Tensor exist but with wrong status"
                ),
            },
            Handle::NotInit => panic!("Cannot get uninitialized handle {id:?}."),
            // The backstop for an `errored` count that has drifted below the
            // map: the guard above is gated on it, so a drift would let an
            // errored entry through to here. Not unreachable — reached only
            // when the invariant `put`/`take` maintain has already broken.
            Handle::Errored(error) => panic!("Tensor {id:?} was never written: {error}"),
        }
    }

    /// Get the tensor handle for the given [tensor intermediate representation](TensorIr).
    pub fn get_tensor_handle(&mut self, tensor: &TensorIr) -> TensorHandle<H> {
        TensorHandle {
            handle: self.get_handle(&tensor.id, &tensor.status),
            shape: tensor.shape.clone(),
        }
    }

    /// Get the [float tensor](burn_backend::backend::BackendTypes::FloatTensorPrimitive) corresponding to the
    /// given [tensor intermediate representation](TensorIr).
    pub fn get_float_tensor<B>(&mut self, tensor: &TensorIr) -> B::FloatTensorPrimitive
    where
        B: BackendIr<Handle = H>,
    {
        B::float_tensor(self.get_tensor_handle(tensor))

View on GitHub (pinned to d16f7ba2ed)

Solutions

  1. Read the embedded `{error}` in the message — it names the original failure from the producing operation; fix that error, not this panic site.
  2. Call `take_error(&tensor)` before consuming tensors in fallible integrations so failures are handled before handle reads.
  3. Audit custom backend code that calls `HandleStore::put`/`take`/`register_errored` for count/map drift if this fires without a prior real error.
  4. Re-run with the producing operation isolated (smaller tensor, different backend) to expose the root failure.
Defensive patterns

Strategy: try-catch

Validate before calling

// poll the deferred failure before consuming:
if let Some(err) = store.take_error(&tensor_ir) {
    return Err(anyhow!("tensor {:?} was never written: {err}", tensor_ir.id));
}

Try / catch

match store.take_error(&tensor_ir) {
    Some(err) => return Err(TensorError::NeverWritten(err)),
    None => store.get_tensor_handle(&tensor_ir), // safe now
}

Prevention

When it happens

Trigger: Consuming a tensor whose producing operation failed and registered `Handle::Errored` (the normal path via the top guard); or, for the line-351 backstop specifically, an `errored` count below the number of Errored map entries so the guard is bypassed.

Common situations: A GPU/kernel op failed earlier (OOM, invalid args, async launch error) and the failure only surfaces when the result tensor is read; error bookkeeping drift in a custom BackendIr/runtime integration.

Related errors


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