{"record":{"id":"fa55229498a8b442","repo":"tursodatabase/turso","slug":"named-parameters-are-not-supported-use-positional","errorCode":null,"errorMessage":"Named parameters are not supported; use positional parameters with '?'","messagePattern":"Named parameters are not supported; use positional parameters with '\\?'","errorType":"exception","errorClass":"ProgrammingError","httpStatus":null,"severity":"error","filePath":"bindings/python/turso/lib.py","lineNumber":596,"sourceCode":"\n    def _reset_last_result(self) -> None:\n        # Ensure any previous statement is finalized to not leak resources\n        if self._active_stmt is not None:\n            try:\n                self._active_stmt.finalize()\n            except Exception:\n                pass\n        self._active_stmt = None\n        self._active_has_rows = False\n        self._description = None\n        self._rowcount = -1\n        # Do not reset lastrowid here; sqlite3 preserves lastrowid until next insert.\n\n    @staticmethod\n    def _to_positional_params(parameters: Sequence[Any] | Mapping[str, Any]) -> tuple[Any, ...]:\n        if isinstance(parameters, Mapping):\n            # Named placeholders are not supported\n            raise ProgrammingError(\"Named parameters are not supported; use positional parameters with '?'\")\n        if parameters is None:\n            return ()\n        if isinstance(parameters, tuple):\n            return parameters\n        # Convert arbitrary sequences to tuple efficiently\n        return tuple(parameters)\n\n    @staticmethod\n    def _bind_named_params(stmt: PyTursoStatement, parameters: Mapping[str, Any]) -> None:\n        \"\"\"\n        Bind mapping-style parameters to a prepared SQLite statement, emulating\n        the behavior of Python's ``sqlite3`` module for named parameters.\n\n        SQLite supports the following parameter syntaxes:\n\n            :name\n            @name\n            $name","sourceCodeStart":578,"sourceCodeEnd":614,"githubUrl":"https://github.com/tursodatabase/turso/blob/bad083fafbefdeae9a42ec19bdaaad8918dcf411/bindings/python/turso/lib.py#L578-L614","documentation":"Cursor._to_positional_params raises ProgrammingError when it receives a Mapping (dict-style parameters), because the positional binding path only supports `?` placeholders fed by sequences. In the current code the public execute()/executemany() route Mapping parameters through _bind_named_params (which binds :name/@name/$name/?N placeholders), so this message comes from the positional conversion path — direct use of the private helper, or call paths/versions that bypass the named binder.","triggerScenarios":"Passing a dict to a binding path that only handles sequences, e.g. calling Cursor._to_positional_params({'id': 1}) directly, or helper code that forwards parameters into a sequence-only bind. With `?`-style SQL you must pass a tuple/list: execute(\"... WHERE id = ?\", (1,)).","commonSituations":"Wrapper libraries that normalize parameters by routing everything through one positional binder; porting code that mixes dict parameters with ? placeholders; internal utilities calling private Cursor helpers.","solutions":["For `?`-style SQL pass a sequence: cur.execute(\"SELECT * FROM t WHERE id = ?\", (1,))","For dict parameters keep named placeholders, which the public execute() supports: cur.execute(\"SELECT * FROM t WHERE id = :id\", {\"id\": 1})","If you maintain a wrapper, route Mapping inputs to the named style and sequences to the positional style instead of forcing everything positional"],"exampleFix":"# before\ncur.execute(\"SELECT * FROM t WHERE id = ?\", {\"id\": 1})  # dict with ? placeholder\n\n# after (positional)\ncur.execute(\"SELECT * FROM t WHERE id = ?\", (1,))\n# after (named)\ncur.execute(\"SELECT * FROM t WHERE id = :id\", {\"id\": 1})","handlingStrategy":"validation","validationCode":"import re\nfrom collections.abc import Mapping\n\ndef execute_with_params(cur, sql: str, params):\n    \"\"\"Route dict params to named placeholders, sequences to positional.\"\"\"\n    if isinstance(params, Mapping):\n        if not re.search(r\"[:@$]\\w+\", sql):\n            raise ValueError(\"dict parameters require :name/@name/$name placeholders, not '?'\")\n    return cur.execute(sql, params)","typeGuard":"from collections.abc import Mapping, Sequence\n\ndef is_positional_params(params) -> bool:\n    \"\"\"True when params can feed '?' placeholders (a plain sequence, not a mapping).\"\"\"\n    return isinstance(params, Sequence) and not isinstance(params, (str, bytes)) and not isinstance(params, Mapping)","tryCatchPattern":null,"preventionTips":["Pick one placeholder style per call site: '?' with tuples/lists, or :name with dicts","Public execute() binds named params via :name/@name/$name — keep dicts away from '?' SQL","In wrapper libraries, branch on Mapping vs Sequence instead of forcing one path"],"tags":["python","parameters","db-api","named-params","placeholders"],"backgroundTag":"unsupported-parameter-style","analyzedSha":"bad083fafbefdeae9a42ec19bdaaad8918dcf411","analyzedAt":"2026-08-16T23:12:11.798Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}