Each call into opencloud_token_store is wrapped in its own asyncio.run()
(app/app_config.py, main.py's token popover), so every call gets a fresh
event loop. The asyncpg connection pool is bound to the loop it was created
in, so the next asyncio.run() could try to reuse a connection from an
already-closed loop and fail — unrelated to actual DB availability, but the
broad except Exception in resolve_opencloud_access_token() silently
swallowed it and fell through to "token not found", masking the real cause.
- add_token/get_token/remove_token/list_usernames now call engine.dispose()
in a finally block so each asyncio.run() starts with a clean pool.
- resolve_opencloud_access_token() now surfaces the real exception via
st.error() before falling back to the .env token (kept only for local dev
without a DB).
- Added tests/unit/test_opencloud_token_store.py covering that dispose() is
called after every store operation, including when commit() raises.