{"record":{"id":"1ba987a791e4a186","repo":"jax-ml/jax","slug":"expected-named-tuple-got-s","errorCode":null,"errorMessage":"Expected named tuple, got %s.","messagePattern":"Expected named tuple, got (.+?)\\.","errorType":"validation","errorClass":"std::invalid_argument","httpStatus":null,"severity":"error","filePath":"jaxlib/pytree.cc","lineNumber":1062,"sourceCode":"        if (!IsSortedPyDictKeysEqual(keys, node.sorted_dict_keys)) {\n          // Convert to a nb::list for nb::repr to avoid having to stringify a\n          // vector. This is error path so it is fine to pay conversion cost.\n          throw std::invalid_argument(absl::StrFormat(\n              \"Dict key mismatch; expected keys: %s; present keys: %s.\",\n              nb::cast<std::string_view>(\n                  nb::repr(nb::cast(node.sorted_dict_keys))),\n              nb::cast<std::string_view>(nb::repr(nb::cast(keys)))));\n        }\n        for (nb::handle key : keys) {\n          agenda.push_back(dict[key]);\n        }\n        break;\n      }\n\n      case PyTreeKind::kNamedTuple: {\n        if (!nb::isinstance<nb::tuple>(object) ||\n            !nb::hasattr(object, \"_fields\")) {\n          throw std::invalid_argument(\n              absl::StrFormat(\"Expected named tuple, got %s.\",\n                              nb::cast<std::string_view>(nb::repr(object))));\n        }\n        nb::tuple tuple = nb::borrow<nb::tuple>(object);\n        if (tuple.size() != node.arity) {\n          throw std::invalid_argument(absl::StrFormat(\n              \"Named tuple arity mismatch: %d != %d; tuple: %s.\", tuple.size(),\n              node.arity, nb::cast<std::string_view>(nb::repr(object))));\n        }\n        if (tuple.type().not_equal(node.node_data)) {\n          throw std::invalid_argument(absl::StrFormat(\n              \"Named tuple type mismatch: expected type: %s, tuple: %s.\",\n              nb::cast<std::string_view>(nb::repr(node.node_data)),\n              nb::cast<std::string_view>(nb::repr(object))));\n        }\n        for (nb::handle entry : tuple) {\n          agenda.push_back(nb::borrow<nb::object>(entry));\n        }","sourceCodeStart":1044,"sourceCodeEnd":1080,"githubUrl":"https://github.com/jax-ml/jax/blob/1e1c6a8fc06dfcd1247076ec5cae4640cea5d7bb/jaxlib/pytree.cc#L1044-L1080","documentation":"JAX's FlattenUpTo reached a kNamedTuple node, but the object at that position is not a tuple subclass with a _fields attribute (i.e. not a collections.namedtuple/typing.NamedTuple). Plain tuples, lists, and dataclasses fail this check.","triggerScenarios":"Flattening against a treedef that captured a namedtuple while the runtime value is a plain tuple, list, or dataclass instance; occurs when converting namedtuples to dataclasses or plain tuples during refactors while reusing treedefs/compiled functions, or when a namedtuple import path resolves to a different object.","commonSituations":"Migrating NamedTuple configs to @dataclass without re-tracing, replacing namedtuple(a, b)(...) with (a, b) for brevity, namedtuple field renaming creating new types, pickling treedefs across code versions.","solutions":["Pass an instance of the same namedtuple type at that position.","Re-derive the treedef after switching container kinds (dataclass vs namedtuple): register dataclasses with jax.tree_util.register_dataclass or use flax.struct.dataclass.","Keep a single canonical type definition module to avoid duplicate namedtuple definitions with identical names but different identity.","If you need struct-like flexibility, use pytree-registered dataclasses instead of namedtuples."],"exampleFix":"# before\nfrom collections import namedtuple\nP = namedtuple('P', ['x', 'y'])\ntreedef = jax.tree_util.tree_structure(P(1, 2))\nobj = (3, 4)                       # plain tuple -> error\n# after\nobj = P(3, 4)                      # same namedtuple type","handlingStrategy":"type-guard","validationCode":"from collections import namedtuple\nassert hasattr(obj, '_fields') and isinstance(obj, tuple), f'expected namedtuple, got {type(obj).__name__}'","typeGuard":"import typing\ndef is_namedtuple_instance(x) -> bool:\n    return isinstance(x, tuple) and hasattr(x, '_fields')","tryCatchPattern":"try:\n    treedef.flatten_up_to(obj)\nexcept (ValueError, TypeError) as e:\n    if 'Expected named tuple' in str(e):\n        obj = MyNamedTuple(*obj)  # rebuild as the traced namedtuple type\n    else:\n        raise","preventionTips":["Define namedtuple/NamedTuple types once at module top level.","Prefer flax.struct.dataclass or registered dataclasses for evolving structures.","Never alternate between plain tuples and namedtuples for the same field."],"tags":["jax","pytree","type-mismatch","namedtuple"],"backgroundTag":"pytree-structure-mismatch","analyzedSha":"1e1c6a8fc06dfcd1247076ec5cae4640cea5d7bb","analyzedAt":"2026-08-27T09:53:25.647Z","schemaVersion":2},"datasetVersion":"2026-08-27T13:17:12.746Z"}