Troubleshooting

The errors below are raised on purpose, at the point where Tesseract-Torch can still say what went wrong. Each entry lists the message you will see, why it happens, and what to do about it.

apply_tesseract inside a torch.func transform

RuntimeError: apply_tesseract does not support torch.func transforms (torch.func.vjp, torch.func.jvp, torch.func.grad, etc.). Use the standard autograd API instead:
  - Reverse mode: result['y'].backward() or torch.autograd.grad()
  - Forward mode: torch.autograd.forward_ad (dual tensors)

Cause. torch.func transforms (torch.func.vjp, torch.func.jvp, torch.func.grad, and torch.func.jacrev / torch.func.jacfwd, which build on them) trace your function with functionalized tensors that have no backing storage. A Tesseract endpoint receives NumPy arrays, and such a tensor cannot be converted into one. Batched backward (torch.autograd.functional.jacobian(..., vectorize=True), or is_grads_batched=True in torch.autograd.grad) hands the backward pass the same kind of tensor and raises the same error. torch.vmap is the exception; see the next entry.

Fix. Use PyTorch’s standard autograd API, which apply_tesseract supports in both modes:

# Reverse mode
x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)
result = apply_tesseract(tess, inputs={"x": x})
(grad_x,) = torch.autograd.grad(result["y"].sum(), x)

# Forward mode
import torch.autograd.forward_ad as fwAD

with fwAD.dual_level():
    x_dual = fwAD.make_dual(torch.tensor([1.0, 2.0, 3.0]), torch.ones(3))
    result = apply_tesseract(tess, inputs={"x": x_dual})
    _, jvp = fwAD.unpack_dual(result["y"])

torch.vmap without a vmap_method

NotImplementedError: torch.vmap over apply_tesseract needs a batching strategy. Pass vmap_method (one of ['sequential', 'expand_dims', 'broadcast_all']) to apply_tesseract.

Cause. A Tesseract call can be batched in more than one way, and which ones work depends on the Tesseract’s schema, so apply_tesseract does not pick one for you.

Fix. Pass vmap_method to the apply_tesseract call inside the batched function. "sequential" works with any schema; see Batching with torch.vmap for the faster options. If the batched function also uses torch.func.grad, torch.func.jacrev or torch.func.jacfwd, see the previous entry: those transforms stay unsupported under torch.vmap.

xs = torch.randn(8, 3)
ys = torch.vmap(lambda x: apply_tesseract(tess, {"x": x}, vmap_method="sequential")["y"])(xs)

Higher-order derivatives

RuntimeError: apply_tesseract does not support higher-order derivatives: a Tesseract's vector_jacobian_product cannot itself be differentiated. Gradients computed with create_graph=True can be used, but not differentiated again.

Cause. A gradient taken with create_graph=True was differentiated again, for example to get a Hessian-vector product or a gradient penalty. The Tesseract’s share of that gradient comes from its vector_jacobian_product endpoint, which PyTorch cannot differentiate, so a second derivative would leave that share out.

Fix. Differentiate through apply_tesseract only once. Where a second derivative is needed, the Tesseract has to compute it, for example by exposing the gradient as an output of its own.

Reverse-mode AD against a Tesseract without vector_jacobian_product

NotImplementedError: Vector Jacobian Product (VJP) not implemented for this Tesseract.

Cause. .backward() and torch.autograd.grad are dispatched to the Tesseract’s vector_jacobian_product endpoint. The Tesseract you called only defines apply, so there is nothing to dispatch to. The forward pass itself succeeds; the error appears when the backward pass runs.

Fix. Either implement vector_jacobian_product in the Tesseract, or do not ask for gradients through it: pass inputs without requires_grad=True and it behaves as a plain, non-differentiable operation.

Forward-mode AD against a Tesseract without jacobian_vector_product

NotImplementedError: Jacobian Vector Product (JVP) not implemented for this Tesseract.

Cause. The forward-mode counterpart of the previous entry: a call inside torch.autograd.forward_ad.dual_level() with dual tensors as inputs is dispatched to the Tesseract’s jacobian_vector_product endpoint, which this Tesseract does not define.

Fix. Implement jacobian_vector_product in the Tesseract, or pass plain (non-dual) tensors.

List-valued differentiable fields

NotImplementedError: List-valued differentiable inputs are not supported yet: xs.[]. Use a dict-valued field, or keep the list entries as separate schema fields.

Cause. Tesseract-Torch walks nested dictionaries and sub-models to find differentiable leaves, but a list[Differentiable[...]] field arrives as a single opaque value, so its tensors cannot be registered with autograd. The same applies to list-valued differentiable outputs.

Fix. Change the schema to a dict-valued field, or to one field per entry:

class InputSchema(BaseModel):
    xs: dict[str, Differentiable[Array[(3,), Float32]]]

and call it with {"xs": {"first": x0, "second": x1}}.

Forward-mode AD with an in-process PyTorch Tesseract

RuntimeError: Error running Tesseract API jacobian_vector_product: Nested forward mode AD is not supported at the moment

Cause. Tesseract.from_tesseract_api(...) runs the Tesseract in your own Python process. If its jacobian_vector_product is itself implemented with torch.func.jvp (as the Tesseracts in examples/ are), that call opens a second forward-AD level inside your dual_level() block, which PyTorch refuses.

Fix. Run the Tesseract in its own process. Tesseract.from_source("path/to/tesseract_api.py") does that without Docker, and python_executable=sys.executable makes it reuse your current environment instead of building one from the Tesseract’s requirements. A container (Tesseract.from_image(...)) works too and is how the examples run it. Both need .serve() or a with block before use. Reverse-mode AD is unaffected either way.