{"record":{"id":"9150e99e1743aa31","repo":"cocoindex-io/cocoindex","slug":"expected-tp-loc-got-type-value-name-v","errorCode":null,"errorMessage":"expected {tp}{loc}, got {type(value).__name__}: {value!r}","messagePattern":"expected (.+?)(.+?), got (.+?): (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"python/cocoindex/_internal/datatype.py","lineNumber":321,"sourceCode":"        return check_none\n\n    # Any — accept everything\n    if tp is Any:\n        return lambda _v, _p: None\n\n    # Union: str | int, str | None, etc.\n    if origin in (types.UnionType, typing.Union):\n        sub_fns = [_build_check_fn(a) for a in args]\n\n        def check_union(value: Any, path: str) -> None:\n            for fn in sub_fns:\n                try:\n                    fn(value, path)\n                    return\n                except TypeError:\n                    continue\n            loc = f\" at {path}\" if path else \"\"\n            raise TypeError(\n                f\"expected {tp}{loc}, got {type(value).__name__}: {value!r}\"\n            )\n\n        return check_union\n\n    # Tuple types\n    if origin is tuple:\n        if len(args) == 2 and args[1] is Ellipsis:\n            # Variable-length: tuple[X, ...]\n            if args[0] is Any:\n\n                def check_var_tuple_any(value: Any, path: str) -> None:\n                    if not isinstance(value, tuple):\n                        loc = f\" at {path}\" if path else \"\"\n                        raise TypeError(\n                            f\"expected tuple{loc}, got {type(value).__name__}\"\n                        )\n","sourceCodeStart":303,"sourceCodeEnd":339,"githubUrl":"https://github.com/cocoindex-io/cocoindex/blob/e84aa99b3292c5270a4b313b2a7137ad9ce8ab3b/python/cocoindex/_internal/datatype.py#L303-L339","documentation":"For a union annotation, TypeChecker tries each member's validator and only accepts the value if one succeeds. If every member raises TypeError, check_union raises a final TypeError naming the full union type, the value's runtime type, and a repr of the value, with an optional path suffix for nested positions.","triggerScenarios":"Passing a value that matches none of the union members — e.g. a key/argument annotated `str | int` receiving bytes, or `str | None` receiving a list — wherever TypeChecker validates stable keys or memo arguments.","commonSituations":"Config values read from JSON/yaml arriving as the wrong scalar type (e.g. int vs str); bytes-vs-str mixups; nested tuple element failing a `str | int` annotation (path like \"[2]\").","solutions":["Coerce the value to one of the union members before passing it (str(x), int(x))","Widen the annotation to include the actual runtime type","Fix the producer so it emits the declared type"],"exampleFix":"// before\nkey = (table, 3.5)  # annotated tuple[str, str | int]\n\n// after\nkey = (table, str(3.5))","handlingStrategy":"validation","validationCode":"def matches_union(v, members) -> bool:\n    return any(isinstance(v, m) for m in members)","typeGuard":"def is_str_or_int(v: object) -> TypeGuard[str | int]:\n    return isinstance(v, (str, int))","tryCatchPattern":"try:\n    checker.check(value)\nexcept TypeError as e:\n    logging.error(\"union mismatch: %s\", e)\n    value = coerce(value)  # str(value) or int(value)","preventionTips":["Coerce JSON/YAML-sourced scalars to declared types before passing","Keep union annotations minimal; prefer a single concrete type when possible","Include TypeChecker.check calls in input-validation tests"],"tags":["python","typing","union","validation"],"backgroundTag":"type-mismatch","analyzedSha":"e84aa99b3292c5270a4b313b2a7137ad9ce8ab3b","analyzedAt":"2026-09-08T15:59:19.997Z","contentChangedAt":"2026-09-08T15:59:19.997Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}