Skip to content

Troubleshooting

Search for the exact error text below, then apply the correction for that failure mode.

Missing sqlalchemy_url in environ.

The default fastsqla.lifespan reads the database URL from SQLALCHEMY_URL. The variable was absent when FastAPI started.

Set an async SQLAlchemy URL before starting the application:

export SQLALCHEMY_URL=sqlite+aiosqlite:///db.sqlite
uvicorn example:app

For configuration in code, create the lifespan explicitly:

from fastapi import FastAPI
from fastsqla import new_lifespan

app = FastAPI(lifespan=new_lifespan("sqlite+aiosqlite:///db.sqlite"))

The asyncio extension requires an async driver to be used

The URL selects a synchronous database driver. For example, sqlite:///db.sqlite loads Python's synchronous pysqlite driver and produces:

The asyncio extension requires an async driver to be used.
The loaded 'pysqlite' is not async.

Install the asynchronous driver for the database and include it in the URL:

Database Install URL prefix
PostgreSQL pip install asyncpg postgresql+asyncpg://
SQLite pip install aiosqlite sqlite+aiosqlite:///
MySQL pip install aiomysql mysql+aiomysql://

Could not locate a bind configured on SQL expression or this Session.

A Session or open_session() operation ran outside the FastSQLA lifespan. The lifespan binds the shared session factory at startup and clears it at shutdown.

Attach the lifespan to FastAPI:

from fastapi import FastAPI
from fastsqla import lifespan

app = FastAPI(lifespan=lifespan)

Use Session only in endpoint parameters. Use open_session() for background work that runs after application startup and finishes before shutdown:

from fastsqla import open_session
from sqlalchemy import select

async def refresh_cache() -> None:
    async with open_session() as session:
        heroes = (await session.scalars(select(Hero))).all()

Do not import or configure SessionFactory; it is an internal lifecycle detail.

MissingGreenlet: greenlet_spawn has not been called

SQLAlchemy attempted implicit database I/O while ordinary Python code accessed an unloaded ORM attribute. This often happens when response serialization touches a lazy-loaded relationship.

Load relationships explicitly inside the awaited query. selectinload() is a good default for collections:

from fastsqla import Session
from sqlalchemy import select
from sqlalchemy.orm import selectinload

async def get_team(team_id: int, session: Session) -> Team:
    stmt = (
        select(Team)
        .where(Team.id == team_id)
        .options(selectinload(Team.heroes))
    )
    return (await session.scalars(stmt)).one()

See SQLAlchemy's guidance on preventing implicit I/O with AsyncSession.

Input should be greater than or equal to 0

The built-in Paginate dependency rejects a negative offset. Start at zero:

GET /heroes?offset=0&limit=10

Input should be less than or equal to 100

The built-in Paginate dependency accepts limit values from 1 through 100. Use a smaller value or define an intentional maximum:

from typing import Annotated

from fastapi import Depends
from fastsqla import PaginateType, new_pagination

LargePage = Annotated[
    PaginateType[HeroModel],
    Depends(new_pagination(default_page_size=10, max_page_size=250)),
]

This Session's transaction has been rolled back due to a previous exception

A database operation such as flush() raised an exception, but application code caught it and then tried to keep using the invalid transaction.

Translate the original database error into an exception and let FastSQLA roll back:

from fastapi import HTTPException
from fastsqla import Session
from sqlalchemy.exc import IntegrityError

async def create_hero(new_hero: HeroInput, session: Session) -> Hero:
    hero = Hero(**new_hero.model_dump())
    session.add(hero)
    try:
        await session.flush()
    except IntegrityError as exc:
        raise HTTPException(status_code=409, detail="Hero already exists") from exc
    return hero

Do not catch and ignore IntegrityError, and do not call commit() inside an endpoint.