{"record":{"id":"b137b12d4802d312","repo":"xai-org/x-algorithm","slug":"got-val-for-union-type-ty","errorCode":null,"errorMessage":"Got {val} for union type {ty}","messagePattern":"Got (.+?) for union type (.+?)","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"phoenix/python/common/xai-configlib/src/xai_configlib/__init__.py","lineNumber":418,"sourceCode":"\ndef cast_type(ty: Type[Any], val: str):\n    if ty is bool:\n        if val in (\"False\", \"false\"):\n            return False\n        elif val in (\"True\", \"true\"):\n            return True\n        raise ValueError(f\"Expected bool [True, true, False, false], got {val!r}\")\n    elif is_optional(ty) and val == \"None\":\n        return None\n    elif is_union(ty):\n        subtys = [subty for subty in get_args(ty) if subty is not type(None)]\n        for subty in subtys:\n            try:\n                return cast_type(subty, val)\n            except ValueError:\n                if subty is subtys[-1]:\n                    raise\n        raise TypeError(f\"Got {val} for union type {ty}\")\n    elif is_tuple(ty):\n        if not val:\n            return ()\n        vals = val.split(\",\")\n        tys = get_args(ty)\n        if len(vals) != len(tys):\n            raise TypeError(f\"Tuple {ty} has different number of arguments from given value {vals}\")\n        return tuple(cast_type(a_ty, v) for a_ty, v in zip(tys, vals))\n    elif is_list(ty):\n        if not val:\n            return []\n        vals = val.split(\",\")\n        tys = get_args(ty)\n        return [cast_type(tys[0], x) for x in vals]\n    elif is_dict(ty):\n        kty, vty = get_args(ty)\n        result = {}\n        for item in val.split(\",\"):","sourceCodeStart":400,"sourceCodeEnd":436,"githubUrl":"https://github.com/xai-org/x-algorithm/blob/24c60942c5c5fdad3a6addffb4c6e6d2f228f04f/phoenix/python/common/xai-configlib/src/xai_configlib/__init__.py#L400-L436","documentation":"When the target type is a Union, cast_type tries each member type in order and re-raises if the last one fails, then also raises this TypeError as a fallback when no member could parse the value.","triggerScenarios":"Overriding a field annotated e.g. Optional[int] or Union[int, str-list] with a string that matches none of the member types (note 'None' is handled only at the Optional level, so Union[int, str] with a weird value, or a Union of containers with mismatched element counts).","commonSituations":"Passing a comma list to Union[int, Tuple[int,int]] with wrong arity, or expecting implicit string-to-float conversion in a Union that lacks float.","solutions":["Check the field's Union annotation and supply a value matching one member exactly","Wrap in Optional and pass 'None' if you want null","Fix the value's format (e.g. correct tuple arity '1,2')"],"exampleFix":"# before\nreplace_cli_subs(cfg, [\"crop=1,2,3\"])  # field: Union[int, Tuple[int, int]]\n# after\nreplace_cli_subs(cfg, [\"crop=1,2\"])","handlingStrategy":"validation","validationCode":"from typing import get_args, get_origin, Union, TypeAliasType\n\ndef union_accepts(ty, val: str) -> bool:\n    origin = get_origin(ty)\n    if origin is not Union:\n        return True\n    from xai_configlib import cast_type\n    return any(\n        (lambda ok: ok)(False) if s is type(None) else _try(cast_type, s, val)\n        for s in get_args(ty)\n    )\ndef _try(f, *a):\n    try: f(*a); return True\n    except (ValueError, TypeError): return False","typeGuard":null,"tryCatchPattern":"try:\n    cast_type(field_ty, raw)\nexcept (ValueError, TypeError) as e:\n    print(f\"Override for {field_name} rejected: {e}\")","preventionTips":["Inspect the field's Union annotation before writing overrides","Test overrides in a smoke-test that instantiates the config"],"tags":["config","type-coercion","union","validation"],"backgroundTag":"type-coercion-failed","analyzedSha":"24c60942c5c5fdad3a6addffb4c6e6d2f228f04f","analyzedAt":"2026-08-28T11:40:14.686Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}