{"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":842,"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":824,"sourceCodeEnd":860,"githubUrl":"https://github.com/tursodatabase/turso/blob/c1e59287258d99b309e362a63f48822256e2f65f/bindings/python/turso/lib.py#L824-L860","documentation":"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().","triggerScenarios":"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().","commonSituations":"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.","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"],"exampleFix":"# before\nparams = Cursor._to_positional_params({\"id\": 1})  # ProgrammingError\ncur.execute(\"SELECT * FROM t WHERE id = :id\", params)\n\n# after\ncur.execute(\"SELECT * FROM t WHERE id = :id\", {\"id\": 1})","handlingStrategy":"validation","validationCode":"from collections.abc import Mapping\n\ndef normalize_params(parameters):\n    # Pass mappings straight through; execute() binds them as named params.\n    if isinstance(parameters, Mapping):\n        return parameters\n    return tuple(parameters or ())\n\ncur.execute(sql, normalize_params(user_params))","typeGuard":null,"tryCatchPattern":"from turso.lib import ProgrammingError\n\ntry:\n    cur.execute(sql, params)\nexcept ProgrammingError as e:\n    if \"Named parameters\" in str(e):\n        placeholders = \", \".join(\"?\" for _ in range(len(params)))\n        cur.execute(sql.replace(\":name\", \"?\"), tuple(params.values()))\n    else:\n        raise","preventionTips":["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"],"tags":["python","db-api","named-parameters","argument-validation"],"backgroundTag":"named-parameters-unsupported","analyzedSha":"c1e59287258d99b309e362a63f48822256e2f65f","analyzedAt":"2026-08-20T07:02:18.389Z","contentChangedAt":"2026-08-20T07:02:18.389Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}