tracel-ai/burn · error
Cannot move a tensor from a capture device
Error message
Cannot move a tensor from a capture device
What it means
Capture tensors are placeholders recorded during graph/program capture (the `capture` feature); they never hold materialized device data. Attempting to move such a tensor OFF a capture device is intentionally forbidden — the comment in the source notes capture is one-way: initialized values may be moved onto a capture device, but captured tensors cannot be moved back because there is no underlying data.
Source
Thrown at crates/burn-dispatch/src/macros.rs:235
$crate::BackendTensor::$kind($body)
),
autodiff: $crate::DispatchAutodiffContext::Enabled(
device_ad.checkpointing,
),
}
},
)+
)*
#[cfg(feature = "autodiff")]
(_, $crate::DispatchDevice::Autodiff(_)) => unreachable!("Autodiff should not wrap an autodiff device."),
#[cfg(feature = "autodiff")]
($crate::DispatchTensorKind::Autodiff(..), _) => panic!("Operation not marked for autodiff."),
// Capture is intentionally one-way: initialized values can be moved onto a
// capture device, but captured tensors have no materialized data to move back.
#[cfg(feature = "capture")]
($crate::DispatchTensorKind::Capture(_), _) => {
panic!("Cannot move a tensor from a capture device")
}
}
};
}
/// Handles tensor movement between devices, supporting both same-backend transfers
/// and cross-backend dispatches.
macro_rules! to_device {
($kind:ident, $inner_fn:ident, $tensor:expr, $device:expr, $to_device:ident, |$inner:ident, $device_ident:ident| $body:expr) => {
backend_matrix!(
to_device_arms,
$kind,
$inner_fn,
$tensor,
$device,
$to_device,
|$inner, $device_ident| $body
)View on GitHub (pinned to d16f7ba2ed)
Solutions
- Do not move capture-kind tensors off the capture device; materialize them by executing/replaying the captured graph first, then move the resulting real tensor.
- Restructure code so device transfers happen before capture begins or after replay completes.
- Guard transfers with a kind check and skip or re-record capture tensors instead of moving them.
- If the data exists elsewhere, reference the original initialized tensor rather than its captured placeholder.
Example fix
// before captured_tensor.to_device(&gpu_device); // Capture kind -> panic // after let real = replay(captured_graph); // materialize data first real.to_device(&gpu_device);
Defensive patterns
Strategy: type-guard
Validate before calling
fn can_move(t: &DispatchPrimitive) -> bool {
!matches!(t.kind, DispatchTensorKind::Capture(_))
} Type guard
fn is_capture(t: &DispatchPrimitive) -> bool {
matches!(t.kind, DispatchTensorKind::Capture(_))
} Prevention
- Filter out capture-kind tensors before any to_device loop.
- Materialize captured graphs via replay before transferring results.
- Keep capture-mode and execution-mode code paths clearly separated.
When it happens
Trigger: Invoking a to-device / cross-device move (macros.rs:235 arm) where the source tensor's DispatchTensorKind is Capture(_) — e.g. taking a tensor produced during capture and calling `to_device(other_device)` on it.
Common situations: Post-capture replay code accidentally treating captured placeholders as real tensors; exporting or checkpointing parts of a captured graph by moving tensors to a live device; mixing capture-mode and execution-mode code paths in the same session.
Related errors
- Cannot move between autodiff and non-autodiff instances.
- Operation not marked for autodiff.
- an autodiff float primitive must have an enabled autodiff co
- Unsupported dtype for `bool_from_data`
- Unsupported dtype for `int_from_data`: {:?}
AI-assisted analysis of tracel-ai/burn@d16f7ba2ed (2026-09-05).
Data as JSON: /api/errors/212b20217bd2c0e2.
Report an issue: GitHub.