Skip to content

Commit fbbdc9e

Browse files
octo-patchocto-patchomeraplak
authored
fix(serverless-hono): withWaitUntil must not destroy global state when context has no waitUntil (#1214)
* fix(serverless-hono): withWaitUntil must not destroy global state when context has no waitUntil Two bugs in withWaitUntil: 1. The else branch unconditionally set ___voltagent_wait_until = undefined even when the global already held a value from an outer scope. This silently killed background tasks (observability, logging) in middleware chains where an inner handler passed {} as context. 2. Errors thrown by context.waitUntil (e.g. DataCloneError in Cloudflare Workers) propagated to callers. The test suite expected them to be swallowed; wrapping the call in try/catch aligns implementation with that contract. Fix: - Remove the else branch entirely. When context has no waitUntil the global is left untouched. - Wrap the bound waitUntil call in try/catch to swallow errors. - Simplify the cleanup function to always restore previousWaitUntil unconditionally (no currentWaitUntil guard needed). All 11 tests in wait-until-wrapper.spec.ts now pass. Fixes #1203 * chore: add changeset --------- Co-authored-by: octo-patch <octo-patch@github.com> Co-authored-by: Omer Aplak <omeraplak@gmail.com>
1 parent 71c9f84 commit fbbdc9e

2 files changed

Lines changed: 18 additions & 12 deletions

File tree

.changeset/rude-pianos-marry.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@voltagent/serverless-hono": patch
3+
---
4+
5+
fix(serverless-hono): withWaitUntil must not destroy global state when context has no waitUntil

packages/serverless-hono/src/utils/wait-until-wrapper.ts

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,20 +34,21 @@ export function withWaitUntil(context?: WaitUntilContext | null): () => void {
3434

3535
if (currentWaitUntil && typeof currentWaitUntil === "function") {
3636
// Bind to context to avoid "Illegal invocation" errors
37-
// And allow errors (like DataCloneError) to propagate so caller can handle fallback
38-
globals.___voltagent_wait_until = currentWaitUntil.bind(context);
39-
} else {
40-
globals.___voltagent_wait_until = undefined;
37+
// Wrap in try/catch so errors (like DataCloneError) are swallowed and don't break the caller
38+
const boundWaitUntil = currentWaitUntil.bind(context);
39+
globals.___voltagent_wait_until = (promise: Promise<unknown>) => {
40+
try {
41+
boundWaitUntil(promise);
42+
} catch {
43+
// Swallow errors to avoid breaking the caller
44+
}
45+
};
4146
}
47+
// No else branch — don't touch global when context has no waitUntil,
48+
// to avoid destroying a previously set value from an outer scope
4249

43-
// Return cleanup function
50+
// Return cleanup function that always restores the previous state
4451
return () => {
45-
if (currentWaitUntil) {
46-
if (previousWaitUntil) {
47-
globals.___voltagent_wait_until = previousWaitUntil;
48-
} else {
49-
globals.___voltagent_wait_until = undefined;
50-
}
51-
}
52+
globals.___voltagent_wait_until = previousWaitUntil;
5253
};
5354
}

0 commit comments

Comments
 (0)