tracel-ai/burn · error
an enabled float tensor must use an autodiff primitive
Error message
an enabled float tensor must use an autodiff primitive
What it means
This panic fires inside dispatch's q_matmul when one matmul operand is a Float tensor whose autodiff context is Enabled, but the tensor's backend primitive is not an autodiff primitive. Burn maintains an invariant pairing: a float tensor primitive that supports autodiff must always be accompanied by an enabled autodiff context (and vice versa). Mixing a plain float primitive with an enabled autodiff context means internal state got out of sync, so the library panics rather than silently skipping gradient tracking.
Source
Thrown at crates/burn-dispatch/src/ops/qtensor.rs:302
(TensorPrimitive::QFloat(lhs), TensorPrimitive::QFloat(rhs)) => {
let autodiff = lhs.autodiff.merge(rhs.autodiff);
// With no float input, the first tensor is the routing tensor.
backend_list!(q_matmul_qq_arms, lhs, rhs, autodiff)
}
(TensorPrimitive::Float(lhs), TensorPrimitive::QFloat(rhs)) => {
let autodiff = lhs.autodiff.merge(rhs.autodiff);
#[cfg(feature = "autodiff")]
match (
matches!(&lhs.kind, DispatchTensorKind::Autodiff(_)),
lhs.autodiff,
) {
(true, DispatchAutodiffContext::Enabled(_))
| (false, DispatchAutodiffContext::Disabled) => {}
(true, DispatchAutodiffContext::Disabled) => {
panic!("an autodiff float primitive must have an enabled autodiff context")
}
(false, DispatchAutodiffContext::Enabled(_)) => {
panic!("an enabled float tensor must use an autodiff primitive")
}
}
backend_list!(q_matmul_fq_arms, lhs, rhs, autodiff)
}
(TensorPrimitive::QFloat(lhs), TensorPrimitive::Float(rhs)) => {
let autodiff = lhs.autodiff.merge(rhs.autodiff);
#[cfg(feature = "autodiff")]
match (
matches!(&rhs.kind, DispatchTensorKind::Autodiff(_)),
rhs.autodiff,
) {
(true, DispatchAutodiffContext::Enabled(_))
| (false, DispatchAutodiffContext::Disabled) => {}
(true, DispatchAutodiffContext::Disabled) => {
panic!("an autodiff float primitive must have an enabled autodiff context")
}
(false, DispatchAutodiffContext::Enabled(_)) => {
panic!("an enabled float tensor must use an autodiff primitive")View on GitHub (pinned to d16f7ba2ed)
Solutions
- Ensure every Float tensor carrying DispatchAutodiffContext::Enabled is created from an autodiff backend primitive (DispatchTensorKind::Autodiff) so the kind and context agree.
- If the tensor truly is a non-autodiff float primitive, construct it with DispatchAutodiffContext::Disabled.
- When mixing operands, verify that merging autodiff contexts (lhs.autodiff.merge(rhs.autodiff)) matches the actual primitive kinds of both sides.
- If this arises inside burn itself, report it — it indicates a broken invariant in the dispatch layer, not user error in a normal op call.
Example fix
// before (mismatched: plain primitive + enabled context)
let t = BackendTensor::Float(float_primitive); // primitive kind: Float
let t = TensorPrimitive::Float { kind: DispatchTensorKind::Float(..), autodiff: DispatchAutodiffContext::Enabled(ctx) };
// after (consistent)
let t = TensorPrimitive::Float { kind: DispatchTensorKind::Autodiff(autodiff_primitive), autodiff: DispatchAutodiffContext::Enabled(ctx) }; Defensive patterns
Strategy: validation
Validate before calling
// before q_matmul on float operands
fn ensure_ad_consistent(t: &TensorPrimitive<DispatchBackend>) -> Result<(), String> {
let is_ad = matches!(t.tensor().kind, DispatchTensorKind::Autodiff(_));
let ctx_enabled = matches!(&t.tensor().autodiff, DispatchAutodiffContext::Enabled(_));
if is_ad == ctx_enabled { Ok(()) } else { Err(format!("autodiff kind/context mismatch: kind_ad={is_ad}, ctx_enabled={ctx_enabled}")) }
} Type guard
fn is_autodiff_float<B: Backend>(t: &BackendTensor<B>) -> bool {
matches!(t, BackendTensor::Autodiff(_))
|| matches!(t, BackendTensor::Float(f) if matches!(f.kind, DispatchTensorKind::Autodiff(_)))
} Prevention
- Always construct Float tensors through constructors that derive kind and autodiff context from the same primitive.
- Never hand-set DispatchAutodiffContext::Enabled on a plain float primitive.
- After merging autodiff contexts, re-check that the merged state matches the operand primitive kinds.
- Add debug assertions for kind/context consistency in custom backend adapters.
When it happens
Trigger: Calling matmul between two quantized-float (QFloat) tensors or float/QFloat mixes where one TensorPrimitive::Float was built from a non-autodiff backend primitive while its `autodiff` field is DispatchAutodiffContext::Enabled — i.e., the Float tensor's `is_autodiff()`-style check (matches! on DispatchTensorKind::Autodiff) is false but `rhs.autodiff`/`lhs.autodiff` is Enabled.
Common situations: Constructing dispatch tensors manually with mismatched primitive/kind and autodiff context; wrapping a plain (non-autodiff) backend tensor in an Enabled autodiff context; bugs in custom backend adapters or capture/replay paths that rebuild TensorPrimitive::Float incorrectly.
Related errors
- Requires autodiff tensor.
- Should be float, got int
- Should be float, got autodiff
- Should be bool, got autodiff
- an autodiff float primitive must have an enabled autodiff co
AI-assisted analysis of tracel-ai/burn@d16f7ba2ed (2026-09-05).
Data as JSON: /api/errors/61b72d9411895507.
Report an issue: GitHub.