tracel-ai/burn · error

Can't convert a non leaf tensor into a tracked tensor

Error message

Can't convert a non leaf tensor into a tracked tensor

What it means

require_grad can only be called on a leaf tensor (one whose gradient requirement is Grad or None). If the tensor is an intermediate result (Requirement::GradInBackward), it is not a leaf and converting it to a tracked tensor would corrupt the autodiff graph, so the library panics.

Source

Thrown at crates/burn-autodiff/src/tensor.rs:110

            primitive,
            node: node.clone(),
        }
    }

    pub fn is_tracked(&self) -> bool {
        !self.node.requirement.is_none()
    }

    /// Mark the tensor as requiring gradients.
    ///
    /// # Panics
    ///
    /// It panics if the tensor is not a leaf.
    pub fn require_grad(mut self) -> Self {
        match self.node.requirement {
            Requirement::Grad => self,
            Requirement::GradInBackward => {
                panic!("Can't convert a non leaf tensor into a tracked tensor")
            }
            Requirement::None => {
                self.node = Node::new(
                    vec![],
                    0,
                    self.node.id,
                    Requirement::Grad,
                    self.node.properties.clone(),
                    self.node.client.clone(),
                    self.node.distributed_params.clone(),
                )
                .into();
                let step = RootStep::new(self.node.clone());

                self.register_step(step, CheckpointerBuilder::default())
            }
        }
    }

View on GitHub (pinned to d16f7ba2ed)

Solutions

  1. Only call require_grad on leaf tensors created via Tensor::from_* constructors or as module parameters
  2. Detach the intermediate first if you truly need a new tracked root: t.detach().require_grad()
  3. Restructure so gradient requirement is set once at tensor creation, not mid-graph
  4. Keep tensors you intend to toggle as Parameters of a module and toggle the module's grad setting

Example fix

// before
let out = x.matmul(&w);
out.require_grad(); // panic: non-leaf
// after
let out = x.detach().matmul(&w).detach().require_grad(); // or require_grad on leaf inputs only
Defensive patterns

Strategy: validation

Validate before calling

// Only call require_grad on leaf tensors
fn safe_require_grad<T: Backend, const D: usize>(t: AutodiffTensor<T, D>) -> AutodiffTensor<T, D> {
    // if t is a result of arithmetic, detach first
    t.require_grad() // callers must ensure t is a leaf
}

Type guard

fn is_leaf<B: Backend, const D: usize>(t: &AutodiffTensor<B, D>) -> bool {
    // leaves have no parents in the graph; track provenance instead of introspection:
    // return true only for tensors you created via Tensor::from_* or Param
    unimplemented!("track leaf provenance in your code, e.g. via newtype wrapper")
}

Try / catch

// Rust panics are not catchable in the autodiff path; prevent instead:
// keep a Leaf wrapper type:
struct LeafTensor<B: Backend, const D: usize>(AutodiffTensor<B, D>);
impl<B: Backend, const D: usize> LeafTensor<B, D> {
    fn require_grad(self) -> AutodiffTensor<B, D> { self.0.require_grad() }
}

Prevention

When it happens

Trigger: Calling x.require_grad() on the output of another operation (a matmul/conv result) rather than on a freshly created tensor; chaining require_grad after arithmetic in training loops where the intent was to accumulate grads mid-graph.

Common situations: Freezing/unfreezing weights but calling require_grad on cached intermediate tensors; migrating PyTorch .requires_grad_() semantics (which applies to any tensor) to burn where it is leaf-only; hand-written training loops manipulating grad requirements mid-step.

Related errors


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