{"record":{"id":"4c94edf64ebac497","repo":"cocoindex-io/cocoindex","slug":"expected-none-loc-got-type-value-name","errorCode":null,"errorMessage":"expected None{loc}, got {type(value).__name__}","messagePattern":"expected None(.+?), got (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"python/cocoindex/_internal/datatype.py","lineNumber":301,"sourceCode":"\ndef _build_check_fn(tp: Any) -> _CheckFn:\n    \"\"\"\n    Build a validation closure for the given type annotation.\n\n    Returns a function ``(value, path) -> None`` that raises ``TypeError``\n    on mismatch.  *path* carries positional context for error messages\n    (empty string at top level, ``\"[0]\"`` for tuple element 0, etc.).\n    \"\"\"\n    origin = typing.get_origin(tp)\n    args = typing.get_args(tp)\n\n    # NoneType\n    if tp is type(None):\n\n        def check_none(value: Any, path: str) -> None:\n            if value is not None:\n                loc = f\" at {path}\" if path else \"\"\n                raise TypeError(f\"expected None{loc}, got {type(value).__name__}\")\n\n        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","sourceCodeStart":283,"sourceCodeEnd":319,"githubUrl":"https://github.com/cocoindex-io/cocoindex/blob/e84aa99b3292c5270a4b313b2a7137ad9ce8ab3b/python/cocoindex/_internal/datatype.py#L283-L319","documentation":"TypeChecker builds a validator per type annotation. For the NoneType annotation (type(None) / None in a Union), the generated check_none closure raises TypeError when the value is not None, with an optional `at <path>` location suffix. This enforces that a None-typed slot only ever holds None.","triggerScenarios":"A value typed as None (or the None member of a union being checked individually by check_union) receives a non-None value at runtime — e.g. a memoized function declared to return None actually returns something, or a tuple element typed None gets a real value.","commonSituations":"Accidentally annotating a field with `None` instead of the intended type; returning a sentinel value from a function declared -> None; misuse of None as a placeholder type.","solutions":["Fix the annotation to the actual intended type instead of None","Make the value actually None at the call site","If both None and a real type are valid, annotate as `T | None`"],"exampleFix":"// before\ndef cb() -> None:\n    return result  # returns a value\n\n// after\ndef cb() -> Result:\n    return result\n# or return None if the declared type is correct","handlingStrategy":"type-guard","validationCode":"if value is not None:\n    raise TypeError(\"slot annotated None received a non-None value\")","typeGuard":"def is_none(v: object) -> TypeGuard[None]:\n    return v is None","tryCatchPattern":"try:\n    checker.check(value)\nexcept TypeError as e:\n    if \"expected None\" in str(e):\n        value = None\n    else:\n        raise","preventionTips":["Don't use bare `None` as an annotation placeholder; use Optional[T] or the real type","Ensure functions declared -> None don't return values","Validate return values in tests for None-typed memo results"],"tags":["python","typing","none","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"}