tracel-ai/burn · critical

capture tensor operations must run inside CaptureDevice::cap

Error message

capture tensor operations must run inside CaptureDevice::capture_scope

What it means

CaptureDevice requires that all tensor operations on its clients run inside `CaptureDevice::capture_scope`. When code obtains a client via `get_client` (e.g. during client initialization or lazy client creation) and no capture scope has registered a client, this panic fires. It is a misuse guard: the capture graph only exists while a scope is active.

Source

Thrown at crates/burn-capture/src/capture.rs:349

///
/// Capture clients are scope-specific: [`CaptureDevice::capture_scope`] creates the client and
/// installs it with `register_scoped_client` before any tensor operations run. An unscoped client
/// would have no lifecycle owner or completed graph boundary, so it is intentionally unsupported.
#[derive(Clone)]
pub struct CaptureChannel;

impl RouterChannel for CaptureChannel {
    type Device = CaptureDevice;
    type Bridge = CaptureBridge;
    type Client = CaptureClient;

    fn name(_device: &Self::Device) -> String {
        "capture".into()
    }

    fn init_client(_device: &Self::Device) -> Self::Client {
        // `get_client` reaches this only when no capture scope registered its client first.
        panic!("capture tensor operations must run inside CaptureDevice::capture_scope")
    }

    fn get_tensor_handle(tensor: &TensorIr, client: &Self::Client) -> TensorData {
        client
            .state()
            .lock()
            .value(tensor.id)
            .unwrap_or_else(|| panic!("capture tensor {} has no initialized value", tensor.id))
    }

    fn register_tensor(
        client: &Self::Client,
        handle: TensorData,
        _shape: Shape,
        _dtype: DType,
    ) -> RouterTensor<Self::Client> {
        client.register_tensor_data(handle)
    }

View on GitHub (pinned to d16f7ba2ed)

Solutions

  1. Wrap all tensor creation/usage for this device inside `CaptureDevice::capture_scope(|device| { ... })`.
  2. Move module construction or client initialization into the capture scope closure.
  3. Do not return capture-device tensors out of the scope; convert/extract results (e.g. into a tensor on a real backend) inside the scope.

Example fix

// before
let tensor = Tensor::<CaptureDevice, 2>::ones([2, 2], &device);
device.capture_scope(|_d| { /* ... */ });
// after
device.capture_scope(|device| {
    let tensor = Tensor::<CaptureDevice, 2>::ones([2, 2], device);
    // use tensor here
});
Defensive patterns

Strategy: validation

Validate before calling

// Enter the scope before creating any capture tensors
let result = device.capture_scope(|device| {
    let t = Tensor::<CaptureDevice, 2>::ones([2, 2], device);
    t.into_data() // extract results inside the scope
});

Prevention

When it happens

Trigger: Calling any tensor operation on a CaptureDevice tensor outside of `device.capture_scope(...)`; constructing or lazily initializing a CaptureDevice client (init_client path) before entering a capture scope; holding a captured tensor beyond the scope and using it later.

Common situations: Creating capture tensors at module init time before the scope starts; storing captured tensors in a struct field and using them after `capture_scope` returns; mixing CaptureDevice with code paths that internally call `get_client` (e.g. lazy backend init).

Related errors


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