sqlalchemy.exc.MissingGreenlet: greenlet_spawn has not been called; can't call await_only() here means you touched a relationship or expired column that wasn't loaded, and SQLAlchemy tried to run a lazy-load query from synchronous attribute access inside an async session. The durable fix is to decide at query time which relationships you need ( selectinload or joinedload ) instead of letting attribute access trigger I/O. Use awaitable_attrs for the occasional one-off, and lazy="raise" to turn the bug into a loud, early failure. (Written against SQLAlchemy 2.0.x as of late 2026.)
Why does MissingGreenlet happen?
In a sync session, author.books on an unloaded relationship quietly emits a SELECT and returns the list. That works because attribute access is a plain Python call and the driver call blocks.
In an async session, the driver call must be awaited, but author.books is not awaitable. SQLAlchemy's async layer runs sync-style ORM code inside a greenlet so that it can bridge to await .…