Skip to content

Commit c18bdb1

Browse files
committed
Fix queued message loss on non-retryable settings sync failure
Previously, a failed pre-startTurn settings update (metadata, runtime mode, or interaction mode) would call completeDelivery, which removes the message from the outbox for non-retryable errors. Since startTurn already carries the settings in its payload, the message can still be delivered even when a separate settings command fails. Replace completeDelivery calls in settings sync blocks with isRetryableFailure checks that only short-circuit for retryable errors (network/transport issues). Non-retryable settings failures are now ignored, allowing startTurn to proceed with the settings embedded in its input.
1 parent dce16f2 commit c18bdb1

1 file changed

Lines changed: 12 additions & 6 deletions

File tree

apps/mobile/src/state/use-thread-outbox-drain.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,12 @@ export function useThreadOutboxDrain(): void {
118118
}
119119
};
120120

121+
const isRetryableFailure = (result: AtomCommandResult<unknown, unknown>): boolean => {
122+
if (!AsyncResult.isFailure(result)) return false;
123+
const error = Cause.squash(result.cause);
124+
return Cause.hasInterruptsOnly(result.cause) || shouldRetryThreadOutboxDelivery(error);
125+
};
126+
121127
if (!modelSelectionsEqual(settings.modelSelection, thread.modelSelection)) {
122128
const updateResult = await updateThreadMetadata({
123129
environmentId: queuedMessage.environmentId,
@@ -127,8 +133,8 @@ export function useThreadOutboxDrain(): void {
127133
modelSelection: settings.modelSelection,
128134
},
129135
});
130-
if (AsyncResult.isFailure(updateResult)) {
131-
return completeDelivery(updateResult);
136+
if (isRetryableFailure(updateResult)) {
137+
return false;
132138
}
133139
}
134140

@@ -142,8 +148,8 @@ export function useThreadOutboxDrain(): void {
142148
createdAt: queuedMessage.createdAt,
143149
},
144150
});
145-
if (AsyncResult.isFailure(runtimeResult)) {
146-
return completeDelivery(runtimeResult);
151+
if (isRetryableFailure(runtimeResult)) {
152+
return false;
147153
}
148154
}
149155

@@ -157,8 +163,8 @@ export function useThreadOutboxDrain(): void {
157163
createdAt: queuedMessage.createdAt,
158164
},
159165
});
160-
if (AsyncResult.isFailure(interactionResult)) {
161-
return completeDelivery(interactionResult);
166+
if (isRetryableFailure(interactionResult)) {
167+
return false;
162168
}
163169
}
164170

0 commit comments

Comments
 (0)