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 excess

View on GitHub (pinned to 1e1c6a8fc0)

Solutions

  1. 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)
  2. Replace None placeholders with empty sentinel pytrees (e.g. {} or jax.ShapeDtypeStruct(())) that flatten consistently
  3. Normalize both trees so None appears in both or neither
  4. 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

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


AI-assisted analysis of jax-ml/jax@1e1c6a8fc0 (2026-08-27). Data as JSON: /api/errors/c4a79fb6c4219c46. Report an issue: GitHub.