tursodatabase/turso · error · ProgrammingError
Named parameters are not supported; use positional…
Error message
Named parameters are not supported; use positional parameters with '?'
What it means
Cursor._to_positional_params() is the static helper that converts a parameter argument into a positional tuple; its Mapping branch raises ProgrammingError to signal that named placeholders cannot be expressed positionally. In the current code, execute() and executemany() route Mapping arguments through _bind_params, which dispatches dicts to _bind_named_params() and binds them as :name/@name/$name parameters. The guard only fires when the private helper itself is handed a mapping - i.e. code that pre-converts parameters instead of passing the dict straight to execute().
Solutions
- Pass the mapping directly to cur.execute(sql, {'name': value}) - named parameters are supported through _bind_named_params
- If you must pre-convert, build a tuple yourself ordered to match the '?' placeholders
- Upgrade to a current pyturso release if an old execute() raised this for plain dicts
Example fix
# before
params = Cursor._to_positional_params({"id": 1}) # ProgrammingError
cur.execute("SELECT * FROM t WHERE id = :id", params)
# after
cur.execute("SELECT * FROM t WHERE id = :id", {"id": 1}) Defensive patterns
Strategy: validation
Validate before calling
from collections.abc import Mapping
def normalize_params(parameters):
# Pass mappings straight through; execute() binds them as named params.
if isinstance(parameters, Mapping):
return parameters
return tuple(parameters or ())
cur.execute(sql, normalize_params(user_params)) Try / catch
from turso.lib import ProgrammingError
try:
cur.execute(sql, params)
except ProgrammingError as e:
if "Named parameters" in str(e):
placeholders = ", ".join("?" for _ in range(len(params)))
cur.execute(sql.replace(":name", "?"), tuple(params.values()))
else:
raise Prevention
- Never pre-convert dict parameters yourself; hand the dict to execute()
- Do not call private Cursor helpers from application code
- Pin a current pyturso release if upgrading from a version that lacked named-parameter support
When it happens
Trigger: Calling Cursor._to_positional_params({...}) directly (private API misuse); older pyturso versions where execute() routed through this helper before _bind_named_params() was added; helper code that tries to normalize parameters into a tuple before calling execute().
Common situations: A wrapper layer that 'normalizes' DB-API parameters before passing them down; upgrading a codebase that worked around an old pyturso build that did not accept dicts.
Related errors
- query timeout must be non-negative
- size must be non-negative
- Cannot operate on a closed cursor
- executemany() requires a single DML…
- batch mode must be one of
AI-assisted analysis of tursodatabase/turso@c1e5928725 (2026-08-20).
Data as JSON: /api/errors/fa55229498a8b442.
Report an issue: GitHub.
Appendix: source
Thrown at bindings/python/turso/lib.py:842
def _reset_last_result(self) -> None:
# Ensure any previous statement is finalized to not leak resources
if self._active_stmt is not None:
try:
self._active_stmt.finalize()
except Exception:
pass
self._active_stmt = None
self._active_has_rows = False
self._description = None
self._rowcount = -1
# Do not reset lastrowid here; sqlite3 preserves lastrowid until next insert.
@staticmethod
def _to_positional_params(parameters: Sequence[Any] | Mapping[str, Any]) -> tuple[Any, ...]:
if isinstance(parameters, Mapping):
# Named placeholders are not supported
raise ProgrammingError("Named parameters are not supported; use positional parameters with '?'")
if parameters is None:
return ()
if isinstance(parameters, tuple):
return parameters
# Convert arbitrary sequences to tuple efficiently
return tuple(parameters)
@staticmethod
def _bind_named_params(stmt: PyTursoStatement, parameters: Mapping[str, Any]) -> None:
"""
Bind mapping-style parameters to a prepared SQLite statement, emulating
the behavior of Python's ``sqlite3`` module for named parameters.
SQLite supports the following parameter syntaxes:
:name
@name
$nameView on GitHub (pinned to c1e5928725)