psycopg/psycopg2 · error · ValueError
invalid name: {name!r}
Error message
invalid name: {name!r} What it means
Raised by Placeholder.__init__() (lib/sql.py:428) when a named placeholder's name string contains ')'. Named placeholders render as %(name)s, so a ')' would prematurely close the placeholder and break the query syntax (and could enable injection). The constructor scans the name and rejects any containing ')' with ValueError before storing it.
Source
Thrown at lib/sql.py:428
>>> q1 = sql.SQL("insert into table ({}) values ({})").format(
... sql.SQL(', ').join(map(sql.Identifier, names)),
... 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):View on GitHub (pinned to 3a6d9d6ddc)
Solutions
- Sanitize placeholder names: allow only [A-Za-z0-9_] and reject/replace everything else.
- Generate placeholder names from a controlled allowlist or sequential counter rather than user input.
- Validate the name does not contain ')' (and ideally any non-identifier char) before passing it in.
Example fix
// before
ph = sql.Placeholder(user_input)
// after
import re
safe = re.fullmatch(r'[A-Za-z_][A-Za-z0-9_]*', user_input)
if not safe:
raise ValueError('bad placeholder name')
ph = sql.Placeholder(user_input) Defensive patterns
Strategy: validation
Validate before calling
import re
_SAFE_NAME = re.compile(r'[A-Za-z_][A-Za-z0-9_]*')
def safe_placeholder(name):
if not (isinstance(name, str) and _SAFE_NAME.fullmatch(name)):
raise ValueError(f'unsafe placeholder name: {name!r}')
return sql.Placeholder(name) Type guard
import re
def is_valid_placeholder_name(name) -> bool:
return (
isinstance(name, str)
and ')' not in name
and re.fullmatch(r'[A-Za-z_][A-Za-z0-9_]*', name) is not None
) Prevention
- Generate placeholder names from a controlled counter or allowlist, never raw user input.
- Restrict placeholder names to [A-Za-z0-9_] and require a non-digit first char.
- Treat ')' in a name as an injection red flag and reject loudly.
When it happens
Trigger: sql.Placeholder('foo)bar'), sql.Placeholder('a); drop'), or any sql.Placeholder(name) where name contains a ')' character.
Common situations: Building placeholder names from untrusted/unsanitized user input; concatenating user-provided identifiers into placeholder names; copy-paste that includes a closing paren.
Related errors
- no format specification supported by SQL
- no format conversion supported by SQL
- cannot switch from automatic field numbering to manual
- cannot switch from manual field numbering to automatic
- expected string or None as name, got {name!r}
AI-assisted analysis of psycopg/psycopg2@3a6d9d6ddc (2026-08-04).
Data as JSON: /data/errors/33090e04f38334b4.json.
Report an issue: GitHub.