{"record":{"id":"76560f80a77d5538","repo":"pydantic/pydantic","slug":"unexpected-type-of-exclude-value-class-name","errorCode":null,"errorMessage":"Unexpected type of exclude value {class_name}","messagePattern":"Unexpected type of exclude value (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"pydantic/_internal/_utils.py","lineNumber":288,"sourceCode":"            merge_keys = list(base) + [k for k in override if k not in base]\n\n        merged: dict[int | str, Any] = {}\n        for k in merge_keys:\n            merged_item = cls.merge(base.get(k), override.get(k), intersect=intersect)\n            if merged_item is not None:\n                merged[k] = merged_item\n\n        return merged\n\n    @staticmethod\n    def _coerce_items(items: AbstractSetIntStr | MappingIntStrAny) -> MappingIntStrAny:\n        if isinstance(items, Mapping):\n            pass\n        elif isinstance(items, AbstractSet):\n            items = dict.fromkeys(items, ...)  # type: ignore\n        else:\n            class_name = getattr(items, '__class__', '???')\n            raise TypeError(f'Unexpected type of exclude value {class_name}')\n        return items  # type: ignore\n\n    @classmethod\n    def _coerce_value(cls, value: Any) -> Any:\n        if value is None or cls.is_true(value):\n            return value\n        return cls._coerce_items(value)\n\n    @staticmethod\n    def is_true(v: Any) -> bool:\n        return v is True or v is ...\n\n    def __repr_args__(self) -> _repr.ReprArgs:\n        return [(None, self._items)]\n\n\nif TYPE_CHECKING:\n","sourceCodeStart":270,"sourceCodeEnd":306,"githubUrl":"https://github.com/pydantic/pydantic/blob/cc13d1b8c978eaf78ed5308329cd41f03ecc3144/pydantic/_internal/_utils.py#L270-L306","documentation":"Raised by ValueItems._coerce_items when an `include`/`exclude` argument passed to model serialization (model_dump, model_dump_json, model_validate, etc.) is neither a Mapping nor an AbstractSet. The include/exclude API only accepts AbstractSet[int|str] (e.g. a set/frozenset of field names or indexes) or Mapping[int|str, Any] (a dict whose keys are field names/indexes and whose values recurse). Any other concrete type triggers this TypeError at serialization time. The {class_name} placeholder shows the offending value's class via getattr(items, '__class__', '???') — note it reads '__class__' (not '__class__.__name__'), so the rendered text is the raw class object, not a clean name.","triggerScenarios":"Calling MyModel(...).model_dump(exclude=['field1','field2']) with a list instead of a set. Passing exclude=('a','b') (tuple, which is a Sequence but NOT an AbstractSet). Passing include=42 or exclude=some_custom_obj. Nesting a list inside a dict exclude: model_dump(exclude={'outer': ['inner']}) — the inner ['inner'] is a list and hits _coerce_items during recursive merge in ValueItems.merge.","commonSituations":"Developers coming from v1 who used lists for exclude and find v2 stricter. Building exclude structures dynamically and accidentally injecting a list. Confusing Sequence (list/tuple) with AbstractSet (set/frozenset) when reading type hints. Copying exclude values from JSON (which decodes arrays to lists, not sets).","solutions":["If you want to exclude a flat group of fields, pass a set literal: model_dump(exclude={'field1','field2'}) instead of a list.","If you build excludes from JSON/dynamic data, wrap with set(): model_dump(exclude=set(my_list)).","For nested excludes use a dict with sets or Ellipsis as leaf values: model_dump(exclude={'outer': {'inner'}}) or model_dump(exclude={'outer': ...}).","If you genuinely need order/selection of a list, convert each leaf to a set before passing to exclude/include."],"exampleFix":"# before\nmodel.model_dump(exclude=['password', 'token'])\n# after\nmodel.model_dump(exclude={'password', 'token'})\n# or, from JSON data\nmodel.model_dump(exclude=set(json_data['hidden_fields']))","handlingStrategy":"validation","validationCode":"from collections.abc import Mapping, Set\n\ndef normalize_exclude(v):\n    \"\"\"Coerce include/exclude to a set or dict acceptable to pydantic v2.\"\"\"\n    if v is None or v is True or v is ...:\n        return v\n    if isinstance(v, Mapping):\n        return {k: normalize_exclude(x) for k, x in v.items()}\n    if isinstance(v, (set, frozenset)):\n        return set(v)\n    if isinstance(v, (list, tuple)):\n        return set(v)  # leaf list/tuple -> set\n    raise TypeError(f'Unsupported exclude type: {type(v).__name__}')","typeGuard":"from collections.abc import Mapping, Set\ndef is_valid_exclude(v) -> bool:\n    if v is None or v is True or v is ...:\n        return True\n    if isinstance(v, Mapping):\n        return all(is_valid_exclude(x) for x in v.values())\n    return isinstance(v, (Set, list, tuple))  # caller must convert list/tuple","tryCatchPattern":"# TypeError from _coerce_items is a programming error, not transient.\n# Prefer pre-validation (above). If you must catch:\ntry:\n    data = model.model_dump(exclude=normalize_exclude(raw))\nexcept TypeError as e:\n    raise ValueError(f'Bad exclude shape: {e}') from e","preventionTips":["Always pass sets (literals or set()) for flat field selection, never lists/tuples.","When loading excludes from JSON, run set() over every array leaf.","Centralize include/exclude construction in one helper so the shape is validated once.","Add a unit test that asserts model_dump(exclude=...) accepts your dynamic exclude values."],"tags":["pydantic","serialization","exclude","typeerror","include"],"backgroundTag":null,"analyzedSha":"cc13d1b8c978eaf78ed5308329cd41f03ecc3144","analyzedAt":"2026-08-11T16:38:52.905Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}