sqlalchemy/sqlalchemy · error · TypeError

Can only concatenate str (not '{type(self)}') to BitString

Error message

Can only concatenate str (not '{type(self)}') to BitString

What it means

Raised by BitString.__add__ (the `bs + other` operator) at lib/sqlalchemy/dialects/postgresql/bitstring.py:211-216 when the right-hand operand is not a str/BitString. BitString only supports concatenation with string-like operands to keep the result a valid bit string. NOTE: the message prints type(self) (always BitString) instead of type(o), so the reported type is misleading and does not reflect the offending operand.

Source

Thrown at lib/sqlalchemy/dialects/postgresql/bitstring.py:214

        return int(self, 2) if self else 0

    def to_bytes(self, length: int = -1) -> bytes:
        return int(self).to_bytes(
            length if length >= 0 else self.octet_length, byteorder="big"
        )

    def __bytes__(self) -> bytes:
        return self.to_bytes()

    def __getitem__(
        self, key: SupportsIndex | slice[Any, Any, Any]
    ) -> BitString:
        return BitString(super().__getitem__(key), False)

    def __add__(self, o: str) -> BitString:
        """Return self + o"""
        if not isinstance(o, str):
            raise TypeError(
                f"Can only concatenate str (not '{type(self)}') to BitString"
            )
        return BitString("".join([self, o]))

    def __radd__(self, o: str) -> BitString:
        if not isinstance(o, str):
            raise TypeError(
                f"Can only concatenate str (not '{type(self)}') to BitString"
            )
        return BitString("".join([o, self]))

    def __lshift__(self, amount: int) -> BitString:
        """Shifts each the bitstring to the left by the given amount.
        String length is preserved::

            BitString("000101") << 1 == BitString("001010")
        """
        return BitString(

View on GitHub (pinned to d9b44cb731)

Solutions

  1. Convert the operand to a str of '0'/'1' first: bs + ('1' if flag else '0').
  2. For ints use from_int then concatenate, or format explicitly: bs + format(value, 'b').
  3. Do not trust the reported type in this message; inspect both operands manually.

Example fix

// before
result = bits + flag  # flag is int 1
// after
result = bits + ('1' if flag else '0')
Defensive patterns

Strategy: type-guard

Validate before calling

if not isinstance(other, str):
    raise TypeError(f'Cannot concatenate {type(other).__name__} to BitString')
result = bs + other

Type guard

def is_str_like(v) -> bool:
    return isinstance(v, (str, BitString))

Try / catch

try:
    result = bs + other
except TypeError as e:
    if 'concatenate' in str(e):
        result = bs + str(other)  # coerce on purpose

Prevention

When it happens

Trigger: Executing BitString('01') + 1, BitString('01') + None, or BitString('01') + some_object where the right side is an int, bool, bytes, or custom type. Also via f-string/concatenation chains that assume implicit conversion.

Common situations: Assuming BitString behaves like int (it does support __int__) so arithmetic works; concatenating with an int flag instead of '1'/'0'; bytes vs str confusion; relying on the error message to debug and being misled because it always says 'BitString'.

Related errors


AI-assisted analysis of sqlalchemy/sqlalchemy@d9b44cb731 (2026-08-01). Data as JSON: /data/errors/c4985b46664d9f47.json. Report an issue: GitHub.