With this version I got this kind of errors:
app | Fixture cleanup failed for <DAL uri="pgsql://******@xxx.xxx.xx.xx/xxxxx">: '_thread._local' object has no attribute 'request_ctx' > Thread-9 > 11:20:28.761
It is an app that don't use neither flash nor sessions
claude found that:
Root cause
Fixture._local.request_ctx (a per-thread dict) is created lazily, and only by Session.on_request (core.py:974) and Flash.on_request (core.py:601) via Fixture.local_initialize(). The DAL
fixture's on_request (core.py:520-524) never calls it, and DAL doesn't use the thread-local at all.
The drivers app actions use only @action.uses(db) — no session, no flash. So on any worker thread that has never served a Session/Flash request, Fixture._local has no request_ctx attribute
at all.
Then the request-teardown loop runs Fixture.local_delete(db) for every fixture (core.py:1198), which does del Fixture._local.request_ctx[obj]:
@staticmethod
def local_delete(obj):
del Fixture._local.request_ctx[obj] # core.py:483
With no request_ctx attribute, this raises AttributeError: '_thread._local' object has no attribute 'request_ctx'. The loop catches KeyError (dict exists, key missing) but not
AttributeError (dict never created), so it falls through to the generic handler and logs Fixture cleanup failed (core.py:1201-1206).
It's harmless: the DB connection is already committed and recycled in DAL.on_success (which ran fine — that's why you still get 200). Nothing leaks, because DAL never created a
thread-local entry to begin with. It's pure log noise, introduced in commit 1e0494d1 ("Log unexpected fixture cleanup failures").
The fix
The loop's own comment says KeyError is expected when a fixture "never ran (or never initialised a thread-local)". AttributeError is exactly that second case — the dict-level version of
the same benign condition. One-line fix at core.py:1199:
except (KeyError, AttributeError):
pass
Thank you