jax-ml/jax · error · ValueError
the rematted computation's closure contains a mutable array
Error message
the rematted computation's closure contains a mutable array reference of type {v.aval.str_short()} that is not one of the rematted function's inputs, but such refs cannot be saved What it means
PyTreeDef::Compose rejects composing two treedefs whose registries differ. Each PyTreeDef is bound to the PyTreeRegistry it was created under (global or a custom registry), and composition requires them to be identical objects.
Source
Thrown at jax/_src/ad_checkpoint.py:1059
'the prevent_cse argument to jax.checkpoint'))
out_flat = RematTraced(jaxpr, policy, prevent_cse_)(*consts, *args_ft)
return out_avals_ft.update(out_flat).unflatten()
return decorator
def _static_argnums(f, argnums, argnames) -> frozenset[int]:
argnums = set(argnums)
if argnames:
sig = api_util.fun_signature(f)
assert sig is not None
argnums |= set(api_util.infer_argnums_and_argnames(sig, None, argnames)[0])
return frozenset(argnums)
def dce(traced, policy):
in_fwd = pe._jaxpr_forwarding(traced.jaxpr)
jaxpr = pe.prune_jaxpr_outputs(traced.jaxpr, [f is None for f in in_fwd])
for v in jaxpr.outvars:
if isinstance(v.aval, AbstractRef):
raise ValueError(
"the rematted computation's closure contains a mutable array "
f"reference of type {v.aval.str_short()} that is not one of the "
"rematted function's inputs, but such refs cannot be saved")
# dce_jaxpr preserves attached consts (constvars are never pruned).
jaxpr, used = pe.dce_jaxpr(jaxpr, True)
keep = [u or i in {*in_fwd} for i, u in enumerate(used)]
kept_idx = {i: p for p, i in enumerate(i for i, k in enumerate(keep) if k)}
in_fwd = tuple(kept_idx[f] if f is not None else None for f in in_fwd)
take = tuple(kept_idx[i] for i, u in enumerate(used) if u)
keep_res, keep_primals = split_list(keep, [traced._num_consts])
res = [r for r, u in zip(traced._consts, keep_res) if u]
return keep_primals, Partial(
partial(_dced, jaxpr, in_fwd, take, traced.out_tree, policy), res)
@source_info_util.extend_name_stack('rematted_computation')
def _dced(jaxpr, in_fwd, take, out_tree, policy, res, *args):
ins = [*res, *args]
outs = RematTraced(jaxpr, policy)(*[ins[i] for i in take])View on GitHub (pinned to 1e1c6a8fc0)
Solutions
- Rebuild both treedefs under the same registry instance
- Pass the registry explicitly when constructing PyTreeDefs so they match
- Register custom node types in the registry the treedefs share
Example fix
# before outer = global_treedef; inner = custom_registry_treedef outer.compose(inner) # raises # after inner = custom_registry.BuildPyTreeDef(...) rebuilt under outer's registry outer.compose(inner)
Defensive patterns
Strategy: validation
Validate before calling
assert outer.registry() is inner.registry(), 'registry mismatch' composed = outer.compose(inner)
Type guard
def same_registry(a, b) -> bool:
return a.registry() is b.registry() Try / catch
try:
outer.compose(inner)
except ValueError as e:
if 'registries' in str(e):
inner = _rebuild_under(inner, outer.registry())
else:
raise Prevention
- Thread a single registry object through all treedef construction
- Check registry identity before Compose/Tuple
- Avoid mixing global-registry and custom-registry treedefs
When it happens
Trigger: outer.Compose(inner) where one treedef came from a custom registry (e.g. forest registry / jax.extend.tree_util) and the other from the global registry, or from two different custom registries.
Common situations: Mixing treedefs produced by jax.tree_util (global registry) with treedefs from a custom PyTreeRegistry instance; library code composing treedefs across module boundaries with different registries.
Related errors
- Effects not supported in partial-eval of `checkpoint`/`remat
- Could not find type: %s.
- numpy masked arrays are not supported as direct inputs to JA
- Python int {value} too large to convert to int64
- Python int {value} too large to convert to int32
AI-assisted analysis of jax-ml/jax@1e1c6a8fc0 (2026-08-27).
Data as JSON: /api/errors/2a1c00bc6715609b.
Report an issue: GitHub.