tursodatabase/turso · error · ProgrammingError

batch mode must be one of {sorted(_BATCH_MODES)} or None, go

Error message

batch mode must be one of {sorted(_BATCH_MODES)} or None, got {mode!r}

What it means

Serverless Python SDK equivalent of the batch-mode validation: _batch_begin_sql maps the batch() mode argument to a BEGIN statement via _BATCH_MODES, and raises ProgrammingError when the mode string matches no known mode. This fails before any HTTP request is made, so nothing reaches the server.

Source

Thrown at serverless/python/turso_serverless/connection.py:73

    "concurrent": "BEGIN CONCURRENT",
}

_TRANSACTION_CONTROL_KEYWORDS = {
    "BEGIN",
    "COMMIT",
    "END",
    "ROLLBACK",
    "SAVEPOINT",
    "RELEASE",
}


def _batch_begin_sql(mode: str | None) -> str | None:
    if mode is None:
        return None
    begin_sql = _BATCH_MODES.get(str(mode).lower())
    if begin_sql is None:
        raise ProgrammingError(
            f"batch mode must be one of {sorted(_BATCH_MODES)} or None, got {mode!r}"
        )
    return begin_sql


def _normalize_batch_statements(
    statements: Iterable[Any],
) -> list[tuple[str, Sequence[Any] | Mapping[str, Any]]]:
    """Normalize batch input to (sql, parameters) pairs. Accepts SQL
    strings and (sql, parameters) pairs, like the JavaScript driver."""
    normalized = []
    for index, statement in enumerate(statements):
        if isinstance(statement, str):
            normalized.append((statement, ()))
            continue
        if (
            isinstance(statement, (tuple, list))
            and len(statement) == 2

View on GitHub (pinned to c1e5928725)

Solutions

  1. Use a mode exactly listed in the error message (matching is done on str(mode).lower()), or None.
  2. Strip whitespace and lowercase your mode value before passing it.
  3. Pass enum .value (a str) not the enum instance if wrapping modes in an Enum.
  4. Consult _BATCH_MODES in serverless/python/turso_serverless/connection.py for the authoritative set for your installed version.

Example fix

# before
await conn.batch(stmts, mode="IMMEDIATE ")
# after
await conn.batch(stmts, mode="immediate")
Defensive patterns

Strategy: validation

Validate before calling

from turso_serverless.connection import _BATCH_MODES
mode = str(mode).strip().lower() if mode is not None else None
if mode is not None and mode not in _BATCH_MODES:
    raise ValueError(f"unsupported batch mode {mode!r}; choose from {sorted(_BATCH_MODES)}")

Type guard

def is_valid_batch_mode(mode: object) -> bool:
    from turso_serverless.connection import _BATCH_MODES
    return mode is None or str(mode).lower() in _BATCH_MODES

Try / catch

try:
    await conn.batch(stmts, mode=mode)
except ProgrammingError as e:
    log.warning("bad batch mode %r: %s", mode, e)
    await conn.batch(stmts, mode=None)

Prevention

When it happens

Trigger: Calling Connection.batch(statements, mode=...) on turso_serverless with an unrecognized mode string such as 'BEGIN', 'write', 'transaction', or an enum whose str() isn't a mode name.

Common situations: Copying mode names from other database drivers (psycopg isolation levels, sqlite3 transaction_mode), typos/case-plus-whitespace issues, and SDK version drift where documented modes changed.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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