tursodatabase/turso · error · ProgrammingError

autocommit must be True, False, or 'LEGACY'

Error message

autocommit must be True, False, or 'LEGACY'

What it means

The Connection.autocommit setter accepts exactly True, False, or the string "LEGACY" (case-sensitive), mirroring the stdlib sqlite3 autocommit attribute. Any other value raises ProgrammingError before any state changes. Note that 1 and 0 pass because they compare equal to True/False.

Source

Thrown at bindings/python/turso/lib.py:384

            raise _map_turso_exception(exc)

    @property
    def in_transaction(self) -> bool:
        try:
            return not self._conn.get_auto_commit()
        except Exception as exc:  # noqa: BLE001
            raise _map_turso_exception(exc)

    # Provide autocommit property for sqlite3-like API (optional)
    @property
    def autocommit(self) -> object | bool:
        return self._autocommit_mode

    @autocommit.setter
    def autocommit(self, val: object | bool) -> None:
        # Accept True, False, or "LEGACY"
        if val not in (True, False, "LEGACY"):
            raise ProgrammingError("autocommit must be True, False, or 'LEGACY'")
        self._autocommit_mode = val
        # If switching to False, ensure a transaction is open
        if val is False:
            self._ensure_transaction_open()
        # If switching to True or LEGACY, nothing else to do immediately.

    def close(self) -> None:
        # In sqlite3: If autocommit is False, pending transaction is implicitly rolled back.
        try:
            if self._autocommit_mode is False and self.in_transaction:
                try:
                    self._exec_ddl_only("ROLLBACK")
                except Exception:
                    # As sqlite3 does, ignore rollback failure on close
                    pass
            self._conn.close()
        except Exception as exc:  # noqa: BLE001
            raise _map_turso_exception(exc)

View on GitHub (pinned to bad083fafb)

Solutions

  1. Normalize the incoming value before assigning: map "true"/"1" to True, "false"/"0" to False, "legacy" (any case) to "LEGACY"
  2. Assign literals directly at the call site: conn.autocommit = True / False / "LEGACY"
  3. Treat any other value as a configuration error and fail fast with the raw value in the message

Example fix

# before
conn.autocommit = os.environ.get("AUTOCOMMIT", "LEGACY")  # "legacy" -> ProgrammingError

# after
modes = {"true": True, "false": False, "legacy": "LEGACY"}
raw = os.environ.get("AUTOCOMMIT", "LEGACY").lower()
if raw not in modes:
    raise ValueError(f"invalid AUTOCOMMIT={raw!r}; use true/false/legacy")
conn.autocommit = modes[raw]
Defensive patterns

Strategy: validation

Validate before calling

_MODES = {"true": True, "false": False, "legacy": "LEGACY"}

def normalize_autocommit(raw) -> object | bool:
    if raw is True or raw is False or raw == "LEGACY":
        return raw
    key = str(raw).strip().lower()
    if key in _MODES:
        return _MODES[key]
    raise ValueError(f"invalid autocommit value {raw!r}; use True/False/'LEGACY'")

conn.autocommit = normalize_autocommit(config["autocommit"])

Prevention

When it happens

Trigger: `conn.autocommit = "legacy"` (wrong case), `conn.autocommit = None`, `conn.autocommit = 2`, `conn.autocommit = "on"/"off"` — typically when the mode comes from a config file, environment variable, or JSON/YAML value instead of a literal.

Common situations: Reading the mode from env/config where casing differs; config formats that turn booleans into strings ("true"/"false"); porting isolation_level-style strings ("DEFERRED" etc.) to the autocommit attribute.

Related errors


AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16). Data as JSON: /api/errors/f56f0b2e8b47c6a7. Report an issue: GitHub.