jax-ml/jax · error · NotImplementedError
Effects not supported in AD of `checkpoint`/`remat`: {disall
Error message
Effects not supported in AD of `checkpoint`/`remat`: {disallowed_effects} What it means
During flatten_up_to, if the treedef expects a None node but xs supplies a non-None object, JAX raises this error. Older JAX treated None as a prefix of anything; modern JAX requires an exact None, and the message suggests a tree_map-based workaround with is_leaf=lambda x: x is None.
Source
Thrown at jax/_src/ad_checkpoint.py:608
jaxpr_jvp = pe.convert_constvars_jaxpr(jaxpr_jvp_)
if isinstance(prevent_cse, tuple):
prevent_cse += (True,) * len(nonzero_tangents)
outs = remat_p.bind(
*jaxpr_jvp_.consts, *primals, *nonzero_tangents, jaxpr=jaxpr_jvp,
prevent_cse=prevent_cse, differentiated=differentiated, policy=policy)
out_primals, out_tangents_ = split_list(outs, [len(jaxpr.outvars)])
out_tangents_ = iter(out_tangents_)
out_tangents = [next(out_tangents_) if nz else ad_util.p2tz(p)
for p, nz in zip(out_primals, out_nz)]
return out_primals, out_tangents
ad.primitive_jvps[remat_p] = remat_jvp
def remat_partial_eval(trace: pe.JaxprTrace, *tracers: core.Tracer,
jaxpr: core.Jaxpr, prevent_cse, **params):
assert not jaxpr.constvars
disallowed_effects = effects.remat_allowed_effects.filter_not_in(jaxpr.effects)
if disallowed_effects:
raise NotImplementedError(
f'Effects not supported in AD of `checkpoint`/`remat`: {disallowed_effects}')
policy = params['policy'] or nothing_saveable
in_unknowns = [not t.is_known() for t in tracers]
jaxpr_known, jaxpr_staged, out_unknowns, out_inst, num_res = \
pe.partial_eval_jaxpr_custom(
jaxpr, in_unknowns, [True] * len(in_unknowns), False, False, policy)
# DCE jaxpr_staged, keeping only instantiated outputs which are unknown
_, out_inst_unknown = partition_list(out_inst, out_unknowns)
jaxpr_unknown, in_used_staged = pe.dce_jaxpr(jaxpr_staged, out_inst_unknown)
used_res, in_used_staged = split_list(in_used_staged, [num_res])
# DCE jaxpr_known, keeping all known outputs but discarding dce'd res
out_used_known = [True] * (len(out_unknowns) - sum(out_unknowns)) + used_res
jaxpr_known, in_used_known = pe.dce_jaxpr(jaxpr_known, out_used_known)
num_res = sum(used_res)
# To avoid precision mismatches in fwd and bwd passes due to XLA excessView on GitHub (pinned to 1e1c6a8fc0)
Solutions
- Apply the documented workaround: jax.tree.map(lambda x, y: None if x is None else f(x, y), a, b, is_leaf=lambda x: x is None)
- Replace None placeholders with empty sentinel pytrees (e.g. {} or jax.ShapeDtypeStruct(())) that flatten consistently
- Normalize both trees so None appears in both or neither
- Pin/upgrade code to the new semantics rather than suppressing — the old behavior will not return
Example fix
# before
jax.tree.map(f, a, b) # a contains None, b has arrays there
# after
jax.tree.map(lambda x, y: None if x is None else f(x, y), a, b,
is_leaf=lambda x: x is None) Defensive patterns
Strategy: fallback
Validate before calling
def none_positions_match(a, b) -> bool:
la = jax.tree.leaves(a, is_leaf=lambda x: x is None)
lb = jax.tree.leaves(b, is_leaf=lambda x: x is None)
return [x is None for x in la] == [x is None for x in lb] Type guard
def is_none_safe_pair(a, b) -> bool:
try:
jax.tree.map(lambda x, y: None, a, b, is_leaf=lambda x: x is None)
return True
except ValueError:
return False Try / catch
try:
out = jax.tree.map(f, a, b)
except ValueError as e:
if 'Expected None, got' in str(e):
out = jax.tree.map(lambda x, y: None if x is None else f(x, y),
a, b, is_leaf=lambda x: x is None)
else:
raise Prevention
- Avoid None as a placeholder inside parameter trees; use empty dicts or sentinel pytrees
- Centralize the documented is_leaf=None workaround in a helper
- Test upgrade paths when moving across JAX releases; the None-prefix removal is a deliberate breaking change
When it happens
Trigger: tree_map(f, tree_with_None, tree_with_values) (or any flatten_up_to where the treedef side has None and xs side has a non-None at the same position) — e.g. params with optional entries set to None mapped against fully-populated values.
Common situations: Upgrading JAX to versions where None-prefix behavior was removed (a known breaking change circa JAX 0.4.x); models with optional parameters (None placeholders) mapped over gradients or optimizer states; config trees mixing None and arrays.
Related errors
- the `static_argnums` argument to `jax.checkpoint` / `jax.rem
- 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
- The names should be exclusive and should not intersect in `n
AI-assisted analysis of jax-ml/jax@1e1c6a8fc0 (2026-08-27).
Data as JSON: /api/errors/c4a79fb6c4219c46.
Report an issue: GitHub.