{"record":{"id":"bebd6478faf76db0","repo":"jax-ml/jax","slug":"out-specs-fn-already-specified","errorCode":null,"errorMessage":"out_specs_fn already specified","messagePattern":"out_specs_fn already specified","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"jax/experimental/colocated_python/func.py","lineNumber":88,"sourceCode":"      out_specs_fn: Callable[..., ShapeDtypeStructTree] | None = None,\n      out_specs_treedef: tree_util.PyTreeDef | None = None,\n      out_specs_leaves: tuple[api.ShapeDtypeStruct, ...] | None = None,\n      devices: Sequence[jax.Device] | xc.DeviceList | None = None,\n  ):\n    \"\"\"Creates a new specialization with overrides.\"\"\"\n    if in_specs_treedef is None:\n      in_specs_treedef = self.in_specs_treedef\n    elif self.in_specs_treedef is not None:\n      raise ValueError(\"in_specs already specified\")\n    if in_specs_leaves is None:\n      in_specs_leaves = self.in_specs_leaves\n    elif self.in_specs_leaves is not None:\n      raise ValueError(\"in_specs already specified\")\n\n    if out_specs_fn is None:\n      out_specs_fn = self.out_specs_fn\n    elif self.out_specs_fn is not None:\n      raise ValueError(\"out_specs_fn already specified\")\n\n    if out_specs_treedef is None:\n      out_specs_treedef = self.out_specs_treedef\n    elif self.out_specs_treedef is not None:\n      raise ValueError(\"out_specs already specified\")\n    if out_specs_leaves is None:\n      out_specs_leaves = self.out_specs_leaves\n    elif self.out_specs_leaves is not None:\n      raise ValueError(\"out_specs already specified\")\n\n    if devices is None:\n      devices = self.devices\n    elif self.devices is not None:\n      raise ValueError(\"devices already specified\")\n    elif not isinstance(devices, xc.DeviceList):\n      devices = xc.DeviceList(tuple(devices))\n\n    return Specialization(","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/jax-ml/jax/blob/1e1c6a8fc06dfcd1247076ec5cae4640cea5d7bb/jax/experimental/colocated_python/func.py#L70-L106","documentation":"Raised by ColocatedFunction.update when out_specs_fn is passed to a specialization that already has an out_specs_fn set. ColocatedPython accumulates in_specs/out_specs/devices across update/specialize calls, and each output-spec slot can only be set once. The error prevents ambiguous conflicting output specifications.","triggerScenarios":"Calling f.out_specs(fn1).out_specs(fn2), or specialize()/update() with out_specs_fn=... after out_specs_fn was already provided (directly or via out_specs=...).","commonSituations":"Chaining builder-style configuration on a colocated_python function, e.g. setting output specs in a helper and then again at the call site; refactoring that splits spec configuration across multiple functions.","solutions":["Remove the second out_specs_fn assignment; each spec may be set only once per specialization chain","If overriding is intended, build a fresh specialization from the original function instead of updating the existing one","Audit helper functions/wrappers that set out_specs_fn on your behalf before you set it again"],"exampleFix":"# before\nf2 = f.update(out_specs_fn=fn1).update(out_specs_fn=fn2)  # ValueError\n# after\nf2 = f.update(out_specs_fn=fn1)\nf3 = f.update(out_specs_fn=fn2)","handlingStrategy":"validation","validationCode":"def safe_out_specs_fn(f, fn):\n    state = f.__self__ if hasattr(f, '__self__') else f\n    # inspect current specialization before updating\n    spec = getattr(state, '_specialization', None) or state\n    if getattr(spec, 'out_specs_fn', None) is not None:\n        raise RuntimeError('out_specs_fn already set; refusing to overwrite')\n    return f.update(out_specs_fn=fn)","typeGuard":"def has_out_specs_fn(specialization) -> bool:\n    return getattr(specialization, 'out_specs_fn', None) is not None","tryCatchPattern":"try:\n    f2 = f.update(out_specs_fn=fn)\nexcept ValueError as e:\n    if 'out_specs_fn already specified' in str(e):\n        f2 = f  # already configured; reuse existing\n    else:\n        raise","preventionTips":["Set each spec slot exactly once per specialization chain","Keep one function that owns configuration; never re-apply it in wrappers","Assert slots are None before assigning in generic config code"],"tags":["jax","colocated-python","api-misuse","configuration"],"backgroundTag":"duplicate-option-specification","analyzedSha":"1e1c6a8fc06dfcd1247076ec5cae4640cea5d7bb","analyzedAt":"2026-08-27T09:53:25.647Z","schemaVersion":2},"datasetVersion":"2026-08-27T13:17:12.746Z"}