tursodatabase/turso · error · TypeError

Cannot get raw connection from SQLAlchemy connection

Error message

Cannot get raw connection from SQLAlchemy connection

What it means

get_sync_connection unwraps a SQLAlchemy Connection down to the raw DBAPI connection via getattr(connection, "connection"). If the passed object has no `connection` attribute, it cannot be a SQLAlchemy Connection and the helper raises TypeError("Cannot get raw connection from SQLAlchemy connection") naming the misuse.

Source

Thrown at bindings/python/turso/sqlalchemy/dialect.py:527

            sync.push()

    Args:
        connection: A SQLAlchemy Connection object

    Returns:
        The underlying turso.sync.ConnectionSync object

    Raises:
        TypeError: If the connection is not a Turso sync connection
    """
    from turso.lib_sync import ConnectionSync

    # Get the raw DBAPI connection
    # SQLAlchemy 2.0: connection.connection.dbapi_connection
    # SQLAlchemy 1.4: connection.connection
    raw_conn = getattr(connection, "connection", None)
    if raw_conn is None:
        raise TypeError("Cannot get raw connection from SQLAlchemy connection")

    # Handle SQLAlchemy 2.0 pooled connection wrapper
    dbapi_conn = getattr(raw_conn, "dbapi_connection", raw_conn)

    if not isinstance(dbapi_conn, ConnectionSync):
        raise TypeError(
            f"Expected turso.sync.ConnectionSync, got {type(dbapi_conn).__name__}. "
            "This function only works with sqlite+turso_sync:// connections."
        )

    return dbapi_conn

View on GitHub (pinned to bad083fafb)

Solutions

  1. Pass a live Connection: `with engine.connect() as conn: sync = get_sync_connection(conn)`
  2. From ORM code use the contextual connection: `get_sync_connection(session.connection())`
  3. If you already hold a ConnectionSync, use it directly — no unwrapping needed

Example fix

# before
engine = create_engine("sqlite+turso_sync:///local.db?remote_url=...")
sync_conn = get_sync_connection(engine)  # TypeError

# after
with engine.connect() as conn:
    sync_conn = get_sync_connection(conn)
    rows = sync_conn.cursor().execute("SELECT 1").fetchall()
Defensive patterns

Strategy: type-guard

Validate before calling

from sqlalchemy.engine import Connection

def is_sa_connection(obj) -> bool:
    """True only for live SQLAlchemy Connection objects (what get_sync_connection needs)."""
    return isinstance(obj, Connection)

Type guard

from sqlalchemy.engine import Connection, Engine
from sqlalchemy.orm import Session

def accepts_get_sync_connection(obj) -> bool:
    if isinstance(obj, (Engine, Session)):
        return False  # common mistakes: pass a Connection instead
    return hasattr(obj, "connection") and hasattr(obj, "execute")

Try / catch

try:
    sync_conn = get_sync_connection(conn)
except TypeError as e:
    if "Cannot get raw connection" in str(e):
        raise TypeError("pass a live SQLAlchemy Connection: with engine.connect() as c: ...") from e
    raise

Prevention

When it happens

Trigger: Calling get_sync_connection(engine) (an Engine), get_sync_connection(session) (a Session), or get_sync_connection(raw_connection) (an already-raw ConnectionSync) — none expose the `.connection` attribute a live SQLAlchemy Connection has.

Common situations: Convenience wrappers that accept 'anything connection-ish'; passing session objects from ORM code; tests passing mocks or fakes that only partially imitate Connection.

Related errors


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