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

  1. Stringify every query value before URL.create: `query={'connect_timeout': str(cfg.timeout)}`.
  2. For booleans use the DB-accepted string form (e.g. `'true'`/`'false'` or `'require'`).
  3. For multi-value parameters pass a list of strings, e.g. `query={'host': ['h1', 'h2']}`.
  4. 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

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


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