{"record":{"id":"68ab487fb11c31af","repo":"astral-sh/ruff","slug":"boolean-conversion-is-not-supported-for-union","errorCode":null,"errorMessage":"Boolean conversion is not supported for union `{}` because `{}` doesn't implement `__bool__` correctly","messagePattern":"Boolean conversion is not supported for union `(.+?)` because `(.+?)` doesn't implement `__bool__` correctly","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/ty_python_semantic/src/types/bool.rs","lineNumber":552,"sourceCode":"                        not_boolable_type.display(db, env)\n                    ),\n                );\n                // TODO: It would be nice to create an annotation here for\n                // where `__bool__` is defined. At time of writing, I couldn't\n                // figure out a straight-forward way of doing this. ---AG\n                diag.sub(sub);\n            }\n            Self::Union { union, .. } => {\n                let first_error = union\n                    .elements(context.db())\n                    .iter()\n                    .find_map(|element| element.try_bool(db, env).err())\n                    .unwrap();\n\n                builder.into_diagnostic(format_args!(\n                    \"Boolean conversion is not supported for union `{}` \\\n                     because `{}` doesn't implement `__bool__` correctly\",\n                    Type::Union(*union).display(db, env),\n                    first_error.not_boolable_type().display(db, env),\n                ));\n            }\n\n            Self::Other { not_boolable_type } => {\n                builder.into_diagnostic(format_args!(\n                    \"Boolean conversion is not supported for type `{}`; \\\n                     it incorrectly implements `__bool__`\",\n                    not_boolable_type.display(db, env)\n                ));\n            }\n        }\n    }\n}\n","sourceCodeStart":534,"sourceCodeEnd":567,"githubUrl":"https://github.com/astral-sh/ruff/blob/15f3fe6b15a5f00172f34b0f542f8ea277f5a586/crates/ty_python_semantic/src/types/bool.rs#L534-L567","documentation":"This is a ty type-checker diagnostic emitted when a boolean conversion (`bool(x)`, `not x`, `if x`, `assert x`) is applied to a value whose type is a union and at least one member's `__bool__` is statically known to be incorrect — e.g. it returns a non-`bool` value. ty reports the full union type plus the specific member that fails the check, via `report_diagnostic_impl` in `types/bool.rs`.","triggerScenarios":"Writing `bool(value)`, `if value:` or `not value` where `value` is inferred as a union (e.g. `A | B`) and one member defines `__bool__` with a wrong return annotation (such as `int` or `str`) or otherwise violates the `__bool__` protocol.","commonSituations":"Unioning a custom class with `None` or another type where the custom class implements `__bool__ -> int`, often after widening a function's return annotation to a union or after a Python/typeshed stubs version bump.","solutions":["Fix the offending member's `__bool__` so it returns exactly `bool`.","Narrow the union before the truthiness test, e.g. `if x is not None:` or an `isinstance` check, so bool conversion applies to a single type.","Adjust the annotation that produced the union if the wider type is unintended.","If the class should rely on `__len__` instead, remove the incorrect `__bool__` rather than returning a non-bool."],"exampleFix":"// before\nclass Flag:\n    def __bool__(self) -> int:  # wrong return type\n        return 1\n\nx: Flag | None\nif x: ...\n\n// after\nclass Flag:\n    def __bool__(self) -> bool:\n        return True\n\nx: Flag | None\nif x is not None:\n    if x: ...","handlingStrategy":"type-guard","validationCode":"# Narrow the union before truthiness testing\ndef check(x: Flag | None) -> None:\n    if x is not None:\n        reveal_type(x)  # Flag, whose __bool__ is valid\n        if x:\n            ...","typeGuard":"from typing import TypeGuard\n\ndef is_flag(x: object) -> TypeGuard[Flag]:\n    return isinstance(x, Flag)","tryCatchPattern":"# ty reports this statically; there is nothing to catch at runtime.\n# Narrow to the member with a correct __bool__ instead of suppressing:\nif isinstance(x, Flag) and bool(x):\n    ...","preventionTips":["Annotate `__bool__` with `-> bool`, never `int` or other truthy types.","Run `ty check` in CI so union bool-conversion issues surface before merge.","Prefer `is not None` narrowing over bare truthiness on optional unions.","Keep custom `__bool__`/`__len__` implementations minimal and consistent with typeshed."],"tags":["python","type-checking","ty","union-type","bool-conversion"],"backgroundTag":"invalid-bool-return-type","analyzedSha":"15f3fe6b15a5f00172f34b0f542f8ea277f5a586","analyzedAt":"2026-09-05T10:32:37.492Z","contentChangedAt":"2026-09-05T10:32:37.492Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}