Skip to content

Commit 51ff968

Browse files
committed
fix(sdk): recover the in-flight message on a preloaded chat.agent retry
A preloaded chat.agent run (started via transport.preload) that retried after an out-of-memory error dropped the message it was processing at crash time. Boot recovery restores the in-flight message into the dispatch queue and advances the .in cursor past it, but the preload branch fired onPreload and waited for a first message without draining that queue, so the recovered message was stranded behind the advanced cursor and the run sat waiting. The preload branch now dispatches a recovered message as the first turn before waiting. Includes a full-stack e2e that reproduces the drop (preloaded run, OOM on attempt 1, in-flight message on .in) and passes with the fix.
1 parent 8c75caf commit 51ff968

3 files changed

Lines changed: 20 additions & 3 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@trigger.dev/sdk": patch
3+
---
4+
5+
Fix a preloaded `chat.agent` run dropping an in-flight message when it retries after an out-of-memory error. The message being processed when the run hit the OOM is now recovered and re-run on the retry, instead of being skipped while the run waited for a new message.

apps/webapp/test/session-agent.e2e.test.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1415,7 +1415,7 @@ describe("session agent e2e (real chat.agent loop)", () => {
14151415
}
14161416
});
14171417

1418-
it("EA21: an OOM fails the run; the attempt-2 retry recovers the message", async () => {
1418+
it("EA21: a preloaded run's OOM retry recovers the in-flight message", async () => {
14191419
const { addressingKey, token, apiKey, baseUrl } = await setupSession(testOomChatAgent.id);
14201420
const runId = `run_oom_${addressingKey}`;
14211421

@@ -1456,7 +1456,6 @@ describe("session agent e2e (real chat.agent loop)", () => {
14561456
modelLocal: testChatModelLocal,
14571457
runId,
14581458
attemptNumber: 2,
1459-
continuation: true,
14601459
});
14611460
try {
14621461
const { parts } = await collectSessionOut({

packages/trigger-sdk/src/v3/ai.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6062,8 +6062,21 @@ function chatAgent<
60626062
);
60636063
}
60646064

6065+
/**
6066+
* A retry (e.g. OOM) or continuation whose boot recovery restored an
6067+
* in-flight `.in` message dispatches it as the first turn. The recovery
6068+
* block already advanced the `.in` cursor past the recovered message,
6069+
* so the preload wait below would otherwise strand it (the run would
6070+
* sit waiting for a "first message" that already arrived).
6071+
*/
6072+
let dispatchedRecoveredFirstTurn = false;
6073+
if (preloaded && bootInjectedQueue.length > 0) {
6074+
currentWirePayload = bootInjectedQueue.shift()!;
6075+
dispatchedRecoveredFirstTurn = true;
6076+
}
6077+
60656078
// Handle preloaded runs — fire onPreload, then wait for the first real message
6066-
if (preloaded) {
6079+
if (preloaded && !dispatchedRecoveredFirstTurn) {
60676080
if (activeSpan) {
60686081
activeSpan.setAttribute("chat.preloaded", true);
60696082
}

0 commit comments

Comments
 (0)