{"record":{"id":"bf58f1ef812e3769","repo":"tursodatabase/turso","slug":"autocommit-must-be-true-false-or-legacy-bf58f1","errorCode":null,"errorMessage":"autocommit must be True, False, or 'LEGACY'","messagePattern":"autocommit must be True, False, or 'LEGACY'","errorType":"exception","errorClass":"ProgrammingError","httpStatus":null,"severity":"error","filePath":"serverless/python/turso_serverless/connection.py","lineNumber":105,"sourceCode":"                sql, args=params, named_args=named_params, want_rows=want_rows\n            )\n        except RuntimeError as e:\n            raise _classify_error(e) from None\n\n    @property\n    def in_transaction(self) -> bool:\n        \"\"\"Whether an explicit transaction is open, from the server's answer\n        as of the most recently completed statement.\"\"\"\n        return not self._session.autocommit\n\n    @property\n    def autocommit(self) -> object | bool:\n        return self._autocommit_mode\n\n    @autocommit.setter\n    def autocommit(self, val: object | bool) -> None:\n        if val not in (True, False, \"LEGACY\"):\n            raise ProgrammingError(\"autocommit must be True, False, or 'LEGACY'\")\n        self._autocommit_mode = val\n\n    def close(self) -> None:\n        if self._closed:\n            return\n        try:\n            # Closing the stream rolls back any open transaction server-side,\n            # matching sqlite3: uncommitted changes are lost on close.\n            self._session.close()\n        finally:\n            self._closed = True\n\n    def commit(self) -> None:\n        self._ensure_open()\n        if self.in_transaction:\n            self._execute_stmt(\"COMMIT\", want_rows=False)\n\n    def rollback(self) -> None:","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/tursodatabase/turso/blob/bad083fafbefdeae9a42ec19bdaaad8918dcf411/serverless/python/turso_serverless/connection.py#L87-L123","documentation":"ProgrammingError raised by the Connection.autocommit setter (connection.py:102-106) when the assigned value is not one of exactly True, False, or the case-sensitive string \"LEGACY\". This follows the Python 3.12+ sqlite3 autocommit contract: True means the server runs every statement in autocommit, False means explicit BEGIN/COMMIT control, and \"LEGACY\" keeps implicit transactions driven by isolation_level. The check uses 'in', so 1 and 0 slip through as True/False, but \"legacy\", \"on\", \"off\", and None do not.","triggerScenarios":"Assigning conn.autocommit = \"on\" / \"off\" / \"legacy\" (wrong case or string form), None, or a config value read as a string. Loading the setting from an env var or JSON/YAML config and assigning it verbatim is the classic trigger.","commonSituations":"Porting configuration from other drivers (psycopg-style toggles, libsql string flags); env-var driven config (os.environ[\"AUTOCOMMIT\"]); deploying the same code across sqlite3 (which accepts only these three values too, so errors usually come from stringly-typed config); typos in the LEGACY literal.","solutions":["Assign one of exactly True, False, or \"LEGACY\" (uppercase)","Map string config values before assignment: on/true/1 -> True, off/false/0 -> False, legacy -> \"LEGACY\"","Validate the value at config load time so the failure names the bad key, not the driver setter"],"exampleFix":"// before\nconn.autocommit = os.environ[\"DB_AUTOCOMMIT\"]  # \"on\" -> ProgrammingError\n\n// after\n_mapping = {\"on\": True, \"off\": False, \"legacy\": \"LEGACY\"}\nconn.autocommit = _mapping[os.environ[\"DB_AUTOCOMMIT\"].lower()]","handlingStrategy":"type-guard","validationCode":"_AUTOCOMMIT_MAP = {\"on\": True, \"off\": False, \"legacy\": \"LEGACY\", \"true\": True, \"false\": False}\n\nraw = os.environ[\"DB_AUTOCOMMIT\"]\nconn.autocommit = _AUTOCOMMIT_MAP[raw.lower()]  # raises KeyError on the config key, not inside the driver","typeGuard":"def is_valid_autocommit(val: object) -> bool:\n    \"\"\"Exactly the three values the setter accepts ('LEGACY' is case-sensitive).\"\"\"\n    return val is True or val is False or (isinstance(val, str) and val == \"LEGACY\")","tryCatchPattern":null,"preventionTips":["Never feed stringly-typed config straight into the setter; map it once at config load","Use 'is True' / 'is False' checks in your own validation so ints don't slip through as bools","Write the literal as \"LEGACY\" — exact uppercase — and cover it with a unit test"],"tags":["python","autocommit","configuration","db-api","validation"],"backgroundTag":"invalid-configuration-value","analyzedSha":"bad083fafbefdeae9a42ec19bdaaad8918dcf411","analyzedAt":"2026-08-16T23:12:11.798Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}