← Debugging Guides

阅读中文版本

SQLAlchemy — DetachedInstanceError when accessing attributes

Resolve SQLAlchemy detached instance errors by controlling session lifetime and eager loading needed attributes.

intermediatedebuggingpythonsqlalchemy

Symptoms

  • DetachedInstanceError: Instance is not bound to a Session
  • Accessing a lazy relationship after the session closes raises the error
  • The object works inside the request but fails in a background task
  • Serialization of an ORM object fails outside the session scope

Possible causes

  • The session was closed and lazy-loaded attributes were accessed afterwards
  • The object was expunged or the session committed and expired state
  • A relationship uses lazy loading that needs an active session to emit SQL
  • An object from one session is passed to code using another session

Diagnosis steps

Find where the attribute is accessed relative to the session

grep -n "with Session\|session.close\|expire_on_commit" app/

Interpretation

If the attribute access is outside the session context manager, the object is detached and lazy loads cannot run.

Reproduce with expiration disabled

python -c "from sqlalchemy import Session; ..." # inspect expire_on_commit

Interpretation

expire_on_commit=True expires attributes after commit, turning later access into a lazy load that fails once detached.

Eager-load the needed relationships

query.options(joinedload(Author.books))

Interpretation

Eager loading fetches related rows inside the session so they remain available after the session closes.

Root cause

ORM objects lazily load attributes only while attached to a session. After the session closes, delayed attribute or relationship access has no session to query, so SQLAlchemy raises DetachedInstanceError.

Fix

Keep the session open for the duration of attribute access, eager-load the relationships you need, or convert detached objects into plain data before leaving the session scope.

Verify the fix

Access the previously failing attributes after the session closes and confirm they no longer raise, and confirm the eager-loaded values match the database.

Symptom walkthrough

An ORM object loads correctly inside a session, but reading an attribute or navigating a relationship later raises DetachedInstanceError: Instance is not bound to a Session. The failure often appears in response serialization, template rendering or background tasks that receive the object after the session has closed or expired.

Fix in practice

Access attributes while the session is open, eager-load the relationships you will need, or convert the object to plain data before leaving the session. Disabling expiration is not a fix by itself, because detached lazy loads still fail; it only prevents post-commit surprises.

Verification

After the change, read the previously failing attributes outside the session and confirm they return data rather than raising. Comparing the loaded values with the database verifies that eager loading fetched the correct rows. Keeping the session boundary close to the data access also keeps the tests honest.

Diagnostic commands

Check the behavior with commands like these:

grep -n "with Session\|session.close\|expire_on_commit" app/
python -c "from sqlalchemy import Session; ..." # inspect expire_on_commit
query.options(joinedload(Author.books))