Files
twenty/packages
Félix Malfait 027a098099 fix(ai): fully roll back a question answer when the resume enqueue fails (#22492)
## Rationale

`answerAgentChatQuestion` is a three-step transaction without the
transaction: resolve the question (flip tool part to `answered`, clear
`pendingQuestionMessageId`, claim the stream), then enqueue the resume
job. If the **enqueue fails**, the catch restores only `activeStreamId`.
What's left behind: tool part says `answered`,
`pendingQuestionMessageId` is `null`, no job will ever run. The client's
own error handler rolls its card back to *pending* — so the user sees an
answerable question whose re-submission deterministically throws
`QUESTION_NOT_PENDING`. The turn is stuck and state is divergent on
three surfaces (DB part, DB thread, client).

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

The failure path was rolling back one of three writes. This makes the
rollback total and **exact**: `resolvePendingQuestion` now returns the
part's precise previous `toolOutput` (no reconstruction guesswork —
question tools can carry arbitrary output fields), and the failure path
restores the part verbatim plus the thread's pending-question state,
guarded on the observed streamId so a competing claim is never
clobbered. After rollback, server and client agree again: the question
is pending, answering retries cleanly.

The audit's alternative — forward recovery (keep the answers, mark the
turn interrupted, resume via Retry) — has nicer UX in isolation but
contradicts the client's existing rollback-to-pending behavior; matching
the established contract wins until the client changes.

## User impact

A transient Redis/queue hiccup at answer time currently bricks the
question turn permanently. With this, the user sees the question again
and can just re-answer.

## Test plan

- [ ] CI green
- [ ] Manual: fail the enqueue (kill Redis briefly) at answer time →
question card returns to pending, re-answer succeeds

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/22492?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:33:20 +02:00
..