Skip to content

Commit 66378e5

Browse files
committed
fix: properly catch top level await errors
async errors within the template and derived etc are properly handled because they know about the last active effect and invoke the error boundary correctly as a response. This logic was missing for our top level await output. Fixes #16613
1 parent 2e02868 commit 66378e5

File tree

5 files changed

+41
-6
lines changed

5 files changed

+41
-6
lines changed

.changeset/silent-suns-whisper.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: properly catch top level await errors

packages/svelte/src/compiler/phases/3-transform/client/transform-client.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -402,9 +402,20 @@ export function client_component(analysis, options) {
402402
params,
403403
b.block([
404404
b.var('$$unsuspend', b.call('$.suspend')),
405-
...component_block.body,
406-
b.if(b.call('$.aborted'), b.return()),
407-
.../** @type {ESTree.Statement[]} */ (template.body),
405+
b.var('$$active', b.id('$.active_effect')),
406+
b.try_catch(
407+
b.block([
408+
...component_block.body,
409+
b.if(b.call('$.aborted'), b.return()),
410+
.../** @type {ESTree.Statement[]} */ (template.body)
411+
]),
412+
b.block([
413+
b.if(
414+
b.unary('!', b.call('$.aborted', b.id('$$active'))),
415+
b.stmt(b.call('$.invoke_error_boundary', b.id('$$error'), b.id('$$active')))
416+
)
417+
])
418+
),
408419
b.stmt(b.call('$$unsuspend'))
409420
]),
410421
true

packages/svelte/src/compiler/utils/builders.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,24 @@ export function throw_error(str) {
659659
};
660660
}
661661

662+
/**
663+
* @param {ESTree.BlockStatement} body
664+
* @param {ESTree.BlockStatement} handler
665+
* @returns {ESTree.TryStatement}
666+
*/
667+
export function try_catch(body, handler) {
668+
return {
669+
type: 'TryStatement',
670+
block: body,
671+
handler: {
672+
type: 'CatchClause',
673+
param: id('$$error'),
674+
body: handler
675+
},
676+
finalizer: null
677+
};
678+
}
679+
662680
export {
663681
await_builder as await,
664682
let_builder as let,

packages/svelte/src/internal/client/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,8 @@ export {
151151
untrack,
152152
exclude_from_object,
153153
deep_read,
154-
deep_read_state
154+
deep_read_state,
155+
active_effect
155156
} from './runtime.js';
156157
export { validate_binding, validate_each_keys } from './validate.js';
157158
export { raf } from './timing.js';
@@ -176,3 +177,4 @@ export {
176177
} from '../shared/validate.js';
177178
export { strict_equals, equals } from './dev/equality.js';
178179
export { log_if_contains_state } from './dev/console-log.js';
180+
export { invoke_error_boundary } from './error-handling.js';

packages/svelte/src/internal/client/reactivity/effects.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -648,7 +648,6 @@ function resume_children(effect, local) {
648648
}
649649
}
650650

651-
export function aborted() {
652-
var effect = /** @type {Effect} */ (active_effect);
651+
export function aborted(effect = /** @type {Effect} */ (active_effect)) {
653652
return (effect.f & DESTROYED) !== 0;
654653
}

0 commit comments

Comments
 (0)