Files
twenty/packages
Félix Malfait ca8ab32253 fix(ai): delete the Redis chunk list on credits-exhausted terminal events (#22477)
## Rationale

The AI chat stream keeps every published chunk in a Redis list
(`agent-chat-stream-chunks:<threadId>`, 1h TTL) so late subscribers can
catch up. On `message-persisted` the list is deleted. On
`credits-exhausted` — the *other* successful terminal event — it wasn't.
Any reload/refetch within the TTL replayed the orphaned chunks, flipping
the thread into a "streaming" state that no terminal event ever closes:
an endless spinner until the user sends another message.

**Production evidence (Sentry):** `Billing Credits Exhausted` fired for
**290 users / 937 events in 90 days**, ongoing
([TWENTY-SERVER-G42](https://twenty-v7.sentry.io/issues/TWENTY-SERVER-G42))
— every one of those users who reloads the chat within an hour hits
this.

## Why this is the root cause, not a symptom patch

The chunk list's lifecycle contract is "cleared when the turn settles".
`credits-exhausted` resolves the job successfully **without** persisting
a `lastStreamError`, so unlike `stream-error` there is no persisted
terminator for catchup to replay after the chunks — the replay is
unconditionally un-closeable. Deleting on both settle events restores
the contract exactly where it's already enforced for
`message-persisted`. `stream-error` deliberately keeps the list: the
persisted error acts as the replay terminator, letting a reloading
client still see the failed turn's partial output.

## User impact

Users who hit their billing cap mid-answer (~100/month) no longer come
back to a permanently spinning thread after a reload — they see the
settled conversation and the billing state.

## Test plan

- [x] Unit spec: chunk accumulation with 1-based seq, deletion on both
terminal events (`it.each`), retention on `stream-error`
- [ ] CI green

https://claude.ai/code/session_01Lyi6zTema2FMVVh8MD6c38

---
_Generated by [Claude
Code](https://claude.ai/code/session_01Lyi6zTema2FMVVh8MD6c38)_

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22477?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-07-02 21:05:19 +02:00
..