{"record":{"id":"3dccb05938c19343","repo":"jax-ml/jax","slug":"linearize-from-jvp-is-a-pair-of-rules-not-a-sin","errorCode":null,"errorMessage":"`linearize_from_jvp` is a pair of rules, not a single rule; unpack it in the class body: `lin, linearized = linearize_from_jvp`","messagePattern":"`linearize_from_jvp` is a pair of rules, not a single rule; unpack it in the class body: `lin, linearized = linearize_from_jvp`","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"jax/_src/hijax.py","lineNumber":663,"sourceCode":"def _vjp_fwd_from_lin(self, nzs_in, *primals):\n  \"\"\"The `vjp_fwd` half of the `vjp_from_lin` pair.\"\"\"\n  return self.lin(nzs_in, *primals)\n\ndef _transpose_linearized(self, residuals, out_ct):\n  \"\"\"The `vjp_bwd_retval` half of the `vjp_from_lin` pair.\"\"\"\n  def tangent_map(*tangents):\n    return self.linearized(residuals, *tangents)\n  zero = lambda x: isinstance(x, ad_util.Zero)\n  out_ct = tree_map(ad_util.instantiate, out_ct, is_leaf=zero)\n  dummies = tree_map(lambda a: ad_util.zeros_like_aval(a.to_tangent_aval()),\n                     self.in_avals)\n  return api.linear_transpose(tangent_map, *dummies)(out_ct)\n\nclass _LinearizeFromJVP(NamedTuple):\n  lin: Callable\n  linearized: Callable\n  def __call__(self, *args, **kwargs):\n    raise TypeError(\n        \"`linearize_from_jvp` is a pair of rules, not a single rule; unpack \"\n        \"it in the class body: `lin, linearized = linearize_from_jvp`\")\n\nclass _VJPFromJVP(NamedTuple):\n  vjp_fwd: Callable\n  vjp_bwd_retval: Callable\n  def __call__(self, *args, **kwargs):\n    raise TypeError(\n        \"`vjp_from_jvp` is a pair of rules, not a single rule; unpack it in \"\n        \"the class body: `vjp_fwd, vjp_bwd_retval = vjp_from_jvp`\")\n\nclass _VJPFromLin(NamedTuple):\n  vjp_fwd: Callable\n  vjp_bwd_retval: Callable\n  def __call__(self, *args, **kwargs):\n    raise TypeError(\n        \"`vjp_from_lin` is a pair of rules, not a single rule; unpack it in \"\n        \"the class body: `vjp_fwd, vjp_bwd_retval = vjp_from_lin`\")","sourceCodeStart":645,"sourceCodeEnd":681,"githubUrl":"https://github.com/jax-ml/jax/blob/1e1c6a8fc06dfcd1247076ec5cae4640cea5d7bb/jax/_src/hijax.py#L645-L681","documentation":"linearize_from_jvp is a NamedTuple pair of rules (_lin_from_jvp, _linearized_from_jvp), not a callable rule. Calling it directly raises this TypeError telling you to unpack it in the class body.","triggerScenarios":"Assigning `lin = linearize_from_jvp` or calling `linearize_from_jvp(...)` directly instead of unpacking `lin, linearized = linearize_from_jvp`.","commonSituations":"Assuming the module-level constant is a single function; using it with defvjp-style single-rule conventions.","solutions":["Unpack in the class body: `lin, linearized = linearize_from_jvp`","If only lin is needed, use `lin, _ = linearize_from_jvp`"],"exampleFix":"# before\nclass P(hijax.HiPrim):\n  lin = linearize_from_jvp  # wrong\n# after\nclass P(hijax.HiPrim):\n  lin, linearized = linearize_from_jvp","handlingStrategy":"validation","validationCode":"from jax._src import hijax\nassert callable(getattr(prim, 'lin', None)) and not isinstance(getattr(type(prim), 'lin', None), hijax._LinearizeFromJVP)","typeGuard":"def lin_rule_valid(p) -> bool:\n    from jax._src import hijax\n    lin = type(p).__dict__.get('lin')\n    return lin is not None and not isinstance(lin, hijax._LinearizeFromJVP)","tryCatchPattern":"try:\n    jax.linearize(f, x)\nexcept TypeError as e:\n    if 'pair of rules' in str(e):\n        raise RuntimeError('unpack: lin, linearized = linearize_from_jvp') from e\n    raise","preventionTips":["Always unpack rule pairs in the class body","Copy the exact unpacking idioms from hijax docs"],"tags":["jax","api-misuse","unpacking"],"backgroundTag":"rule-pair-not-unpacked","analyzedSha":"1e1c6a8fc06dfcd1247076ec5cae4640cea5d7bb","analyzedAt":"2026-08-27T09:53:25.647Z","schemaVersion":2},"datasetVersion":"2026-08-27T13:17:12.746Z"}