astral-sh/ruff · warning

Boolean conversion is not supported for union `{}` because `

Error message

Boolean conversion is not supported for union `{}` because `{}` doesn't implement `__bool__` correctly

What it means

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`.

Source

Thrown at crates/ty_python_semantic/src/types/bool.rs:552

                        not_boolable_type.display(db, env)
                    ),
                );
                // TODO: It would be nice to create an annotation here for
                // where `__bool__` is defined. At time of writing, I couldn't
                // figure out a straight-forward way of doing this. ---AG
                diag.sub(sub);
            }
            Self::Union { union, .. } => {
                let first_error = union
                    .elements(context.db())
                    .iter()
                    .find_map(|element| element.try_bool(db, env).err())
                    .unwrap();

                builder.into_diagnostic(format_args!(
                    "Boolean conversion is not supported for union `{}` \
                     because `{}` doesn't implement `__bool__` correctly",
                    Type::Union(*union).display(db, env),
                    first_error.not_boolable_type().display(db, env),
                ));
            }

            Self::Other { not_boolable_type } => {
                builder.into_diagnostic(format_args!(
                    "Boolean conversion is not supported for type `{}`; \
                     it incorrectly implements `__bool__`",
                    not_boolable_type.display(db, env)
                ));
            }
        }
    }
}

View on GitHub (pinned to 15f3fe6b15)

Solutions

  1. Fix the offending member's `__bool__` so it returns exactly `bool`.
  2. 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.
  3. Adjust the annotation that produced the union if the wider type is unintended.
  4. If the class should rely on `__len__` instead, remove the incorrect `__bool__` rather than returning a non-bool.

Example fix

// before
class Flag:
    def __bool__(self) -> int:  # wrong return type
        return 1

x: Flag | None
if x: ...

// after
class Flag:
    def __bool__(self) -> bool:
        return True

x: Flag | None
if x is not None:
    if x: ...
Defensive patterns

Strategy: type-guard

Validate before calling

# Narrow the union before truthiness testing
def check(x: Flag | None) -> None:
    if x is not None:
        reveal_type(x)  # Flag, whose __bool__ is valid
        if x:
            ...

Type guard

from typing import TypeGuard

def is_flag(x: object) -> TypeGuard[Flag]:
    return isinstance(x, Flag)

Try / catch

# ty reports this statically; there is nothing to catch at runtime.
# Narrow to the member with a correct __bool__ instead of suppressing:
if isinstance(x, Flag) and bool(x):
    ...

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of astral-sh/ruff@15f3fe6b15 (2026-09-05). Data as JSON: /api/errors/68ab487fb11c31af. Report an issue: GitHub.