How Two Async Bugs in Redis Session Storage Wiped 40-Minute AI Conversations
LLM applications that store conversation history in Redis with a fixed TTL will silently drop long-running sessions unless the expiration is refreshed on every read. A parameterized Playwright test that replays real user timing catches these bugs when unit tests cannot.
A production LLM chat app suffered intermittent session loss: users would refresh after a long conversation and find the entire history gone. Unit tests passed because they never replicated the real sequence of send, wait, render, and refresh. A Playwright end-to-end test parameterized for 5 and 15 conversation rounds exposed two bugs: `asyncio.create_task` returned HTTP responses before messages landed in Redis, and `EXPIRE` was set only once at key creation, so active sessions quietly expired mid-conversation.
The fix replaced fire-and-forget tasks with `await` and added a TTL refresh on every history read. Before the fix, 300 Playwright runs failed 84% of the time; after, all 300 passed. Two additional Playwright-specific pitfalls surfaced: missing `path=/` on cookies caused Chromium to drop the session on reload, and `<br>` tags in the DOM broke Playwright's `text=` selector across split text nodes.
The gap between unit tests and production is widest for stateful, async workflows: a store-then-read unit test will never catch a race between an HTTP response and a Redis write.
Cookie behavior differences across browser engines are not theoretical; Playwright reproduced a Chromium-specific cookie drop that manual curl testing missed entirely.
Many 'intermittent' session-loss bugs in LLM apps are deterministic once you control timing — the randomness comes from network and CPU jitter, not from the logic itself.
TTL-based session eviction is a common pattern, but the assumption that '7-day expiry' means '7 days from last use' is wrong unless the code explicitly renews on every access.