django/django · error · TypeError

Lexeme value must be a string, got {value.__class__.__name__

Error message

Lexeme value must be a string, got {value.__class__.__name__}.

What it means

After the empty-string check, `Lexeme.__init__` (search.py:467-470) verifies `isinstance(value, str)` and raises `TypeError` if not, naming the actual class. This catches non-string scalars (int, None, bytes) that would otherwise fail during SQL quoting. The ordering of checks means a non-string non-empty value (e.g. 0, None, an int) reaches this branch.

Source

Thrown at django/contrib/postgres/search.py:468

    def __and__(self, other):
        return self._combine(other, self.BITAND, False)

    def __rand__(self, other):
        return self._combine(other, self.BITAND, True)


class Lexeme(LexemeCombinable, Value):
    _output_field = SearchQueryField()

    def __init__(
        self, value, output_field=None, *, invert=False, prefix=False, weight=None
    ):
        if value == "":
            raise ValueError("Lexeme value cannot be empty.")

        if not isinstance(value, str):
            raise TypeError(
                f"Lexeme value must be a string, got {value.__class__.__name__}."
            )

        if weight is not None and (
            not isinstance(weight, str) or weight.lower() not in {"a", "b", "c", "d"}
        ):
            raise ValueError(
                f"Weight must be one of 'A', 'B', 'C', and 'D', got {weight!r}."
            )

        self.prefix = prefix
        self.invert = invert
        self.weight = weight
        super().__init__(value, output_field=output_field)

    def as_sql(self, compiler, connection):
        param = quote_lexeme(self.value)
        label = ""

View on GitHub (pinned to b5388a3a80)

Solutions

  1. Pass a string: `Lexeme(str(value))` only after confirming the value is non-empty.
  2. Pull the right attribute: `Lexeme(doc.title)` instead of `Lexeme(doc)`.
  3. Decode bytes to str before passing.

Example fix

// before
Lexeme(product.id)
// after
Lexeme(str(product.name))
Defensive patterns

Strategy: type-guard

Validate before calling

if not isinstance(value, str):
    raise TypeError(f'Lexeme value must be str, got {type(value).__name__}')
lex = Lexeme(value)

Type guard

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

Try / catch

try:
    lex = Lexeme(value)
except TypeError as e:
    if 'must be a string' in str(e):
        lex = Lexeme(str(value))
    else:
        raise

Prevention

When it happens

Trigger: Calling `Lexeme(123)`, `Lexeme(None)`, `Lexeme(b'bytes')`, or `Lexeme(some_object)` where the value is not a string. Note: `Lexeme('')` raises the empty error, not this one.

Common situations: Passing an integer ID instead of text; passing None from an optional field; bytes vs str confusion (Python 3); passing a model instance instead of one of its string attributes.

Related errors


AI-assisted analysis of django/django@b5388a3a80 (2026-08-10). Data as JSON: /api/errors/e00935e3dff53ff8. Report an issue: GitHub.