psycopg/psycopg2 · error · TypeError
expected string or None as name, got {name!r}
Error message
expected string or None as name, got {name!r} What it means
Raised by Placeholder.__init__() (lib/sql.py:431) when the name argument is neither a str nor None. A str becomes a named placeholder (%(name)s); None (or omitted) becomes a positional %s. Any other type (int, bytes, list, etc.) has no defined rendering and is rejected with TypeError. Note in particular that an int like 0 does NOT mean 'positional index 0' - positional placeholders take None.
Source
Thrown at lib/sql.py:431
... sql.SQL(', ').join(sql.Placeholder() * len(names)))
>>> print(q1.as_string(conn))
insert into table ("foo", "bar", "baz") values (%s, %s, %s)
>>> q2 = sql.SQL("insert into table ({}) values ({})").format(
... sql.SQL(', ').join(map(sql.Identifier, names)),
... sql.SQL(', ').join(map(sql.Placeholder, names)))
>>> print(q2.as_string(conn))
insert into table ("foo", "bar", "baz") values (%(foo)s, %(bar)s, %(baz)s)
"""
def __init__(self, name=None):
if isinstance(name, str):
if ')' in name:
raise ValueError(f"invalid name: {name!r}")
elif name is not None:
raise TypeError(f"expected string or None as name, got {name!r}")
super().__init__(name)
@property
def name(self):
"""The name of the `!Placeholder`."""
return self._wrapped
def __repr__(self):
if self._wrapped is None:
return f"{self.__class__.__name__}()"
else:
return f"{self.__class__.__name__}({self._wrapped!r})"
def as_string(self, context):
if self._wrapped is not None:
return f"%({self._wrapped})s"
else:View on GitHub (pinned to 3a6d9d6ddc)
Solutions
- For a positional placeholder, pass None or omit the argument: sql.Placeholder() or sql.Placeholder(None).
- For a named placeholder, pass a str.
- Add a type check upstream so only str/None reach Placeholder.
Example fix
// before
ph = sql.Placeholder(0)
// after
ph = sql.Placeholder() # positional %s
# or
ph = sql.Placeholder('name') # named %(name)s Defensive patterns
Strategy: type-guard
Validate before calling
def make_placeholder(name=None):
if name is not None and not isinstance(name, str):
raise TypeError(f'Placeholder name must be str or None, got {type(name).__name__}')
return sql.Placeholder(name) Type guard
def is_valid_placeholder_arg(name) -> bool:
return name is None or isinstance(name, str) Prevention
- Remember Placeholder(0) is NOT positional-by-index - use Placeholder() for %s.
- Omit the argument entirely for positional placeholders to avoid confusion.
- Type-check dynamic names before passing them in.
When it happens
Trigger: sql.Placeholder(0) (mistakenly treating it as a positional index), sql.Placeholder(b'x'), sql.Placeholder(['x']), sql.Placeholder(123).
Common situations: Confusing Placeholder(0) with positional-by-index (it is not); passing bytes names; passing a list/tuple accidentally; copy-paste from code that numbered placeholders.
Related errors
- Identifier cannot be empty
- SQL identifier parts must be strings
- context must be a connection or a cursor
- invalid name: {name!r}
- no format specification supported by SQL
AI-assisted analysis of psycopg/psycopg2@3a6d9d6ddc (2026-08-04).
Data as JSON: /data/errors/9774cc25407246c9.json.
Report an issue: GitHub.