sqlalchemy/sqlalchemy · error · TypeError
Query dictionary values must be strings or sequences of stri
Error message
Query dictionary values must be strings or sequences of strings
What it means
URL._str_dict (engine/url.py:273) validates each value of the query dict passed to URL.create(query=...) / update_query; values must be a str or a Sequence of str (e.g. list/tuple of strings). Any other type (int, dict, None, bool, set) raises TypeError('Query dictionary values must be strings or sequences of strings'). Keys are validated separately ('Query dictionary keys must be strings').
Source
Thrown at lib/sqlalchemy/engine/url.py:273
@overload
def _assert_value(
val: str,
) -> str: ...
@overload
def _assert_value(
val: Sequence[str],
) -> Union[str, Tuple[str, ...]]: ...
def _assert_value(
val: Union[str, Sequence[str]],
) -> Union[str, Tuple[str, ...]]:
if isinstance(val, str):
return val
elif isinstance(val, collections_abc.Sequence):
return tuple(_assert_value(elem) for elem in val)
else:
raise TypeError(
"Query dictionary values must be strings or "
"sequences of strings"
)
def _assert_str(v: str) -> str:
if not isinstance(v, str):
raise TypeError("Query dictionary keys must be strings")
return v
dict_items: Iterable[Tuple[str, Union[Sequence[str], str]]]
if isinstance(dict_, collections_abc.Sequence):
dict_items = dict_
else:
dict_items = dict_.items()
return util.immutabledict(
{
_assert_str(key): _assert_value(View on GitHub (pinned to d9b44cb731)
Solutions
- Stringify every query value before URL.create: `query={'connect_timeout': str(cfg.timeout)}`.
- For booleans use the DB-accepted string form (e.g. `'true'`/`'false'` or `'require'`).
- For multi-value parameters pass a list of strings, e.g. `query={'host': ['h1', 'h2']}`.
- Add a normalization step over the config dict: `{k: str(v) if not isinstance(v, (list, tuple)) else [str(x) for x in v] for k, v in cfg.items()}`.
Example fix
// before
url = URL.create(
'postgresql', username='u', password='p', host='db',
query={'connect_timeout': 10, 'sslmode': True}, # TypeError
)
// after
url = URL.create(
'postgresql', username='u', password='p', host='db',
query={
'connect_timeout': str(10),
'sslmode': 'require',
},
) Defensive patterns
Strategy: validation
Validate before calling
import collections.abc as cabc
def validate_query_dict(query):
"""Ensure every query value is a str or a sequence of str (no bytes, no ints)."""
cleaned = {}
for k, v in dict(query).items():
if not isinstance(k, str):
raise TypeError('Query key %r must be str' % (k,))
if isinstance(v, str) or not isinstance(v, cabc.Sequence):
if not isinstance(v, str):
raise TypeError('Query value for %r must be str or seq of str' % (k,))
cleaned[k] = v
else:
seq = list(v)
if any(not isinstance(e, str) for e in seq):
raise TypeError('All elements in query[%r] must be str' % (k,))
cleaned[k] = tuple(seq)
return cleaned
# usage: URL.create(..., query=validate_query_dict(raw_query)) Type guard
import collections.abc as cabc
def is_valid_query_value(v) -> bool:
if isinstance(v, str):
return True
if isinstance(v, cabc.Sequence):
return all(isinstance(e, str) for e in v)
return False Prevention
- URL query values must be str or tuple/list of str - never ints, floats, bytes, or None.
- Coerce booleans/numbers to 'true'/'1'/etc. explicitly before putting them in the query dict.
- For non-string DBAPI connect args, pass them via create_engine(connect_args={...}) instead of the URL query.
When it happens
Trigger: Passing `URL.create('postgresql', query={'connect_timeout': 10})` (int value), `query={'options': {'k': 'v'}}` (dict value), `query={'sslmode': True}` (bool), or a set as a multi-value. Common when building URLs from typed config objects where numerics/bools aren't stringified.
Common situations: Env/config ingestion without type normalization; passing booleans as Python bools for sslmode/ssl flags; multi-tenant query builders concatenating dicts; pydantic settings models exposing int fields directly into query.
Related errors
- Port argument must be an integer or None
- %s must be a string
- Invalid argument(s) %s sent to create_engine(), using config
- The psycopg2 dialect does not implement ipaddress type handl
- SQLite DateTime type only accepts Python datetime and date o
AI-assisted analysis of sqlalchemy/sqlalchemy@d9b44cb731 (2026-08-01).
Data as JSON: /data/errors/61a954036fc6cca0.json.
Report an issue: GitHub.