From fd9f197c2774fbad8adb2b007dfa46a0b4698533 Mon Sep 17 00:00:00 2001 From: maiieul <45822175+maiieul@users.noreply.github.com> Date: Fri, 26 Jun 2026 07:54:04 +0200 Subject: [PATCH] fix(qwik): unblock vnode-data resume waiters on failure instead of hanging On a vnode-data resume error, logError and markVNodeDataReady so onVNodeDataReady/whenVNodeDataReady waiters fire instead of hanging on the bare throw. --- .changeset/surface-vnode-resume-failures.md | 5 +++++ .../src/core/client/process-vnode-data.ts | 8 ++++---- .../core/client/process-vnode-data.unit.tsx | 19 +++++++++++++++++++ 3 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 .changeset/surface-vnode-resume-failures.md diff --git a/.changeset/surface-vnode-resume-failures.md b/.changeset/surface-vnode-resume-failures.md new file mode 100644 index 00000000000..df7ae9668fd --- /dev/null +++ b/.changeset/surface-vnode-resume-failures.md @@ -0,0 +1,5 @@ +--- +'@qwik.dev/core': patch +--- + +fix: surface a failed vnode-data resume — report it and unblock the `whenVNodeDataReady` waiters — instead of swallowing the error into a silent hang. diff --git a/packages/qwik/src/core/client/process-vnode-data.ts b/packages/qwik/src/core/client/process-vnode-data.ts index e5b72099f22..f6e35509ff0 100644 --- a/packages/qwik/src/core/client/process-vnode-data.ts +++ b/packages/qwik/src/core/client/process-vnode-data.ts @@ -6,6 +6,7 @@ import { getSegmentVNodeRefId, } from '../shared/vnode-data-types'; import type { ElementVNode } from '../shared/vnode/element-vnode'; +import { logError } from '../shared/utils/log'; import type { ContainerElement, QDocument } from './types'; import { createYieldingIteratorState, @@ -143,7 +144,7 @@ export const whenVNodeDataReady = ( }); }; -function enqueueProcessVNodeDataJob( +export function enqueueProcessVNodeDataJob( qDocument: QDocument, iterator: Generator ): void { @@ -177,9 +178,8 @@ function scheduleProcessVNodeData(qDocument: QDocument, state: ProcessVNodeDataS }, (error) => { state.$active$ = null; - qDocument.qVNodeDataReady = qDocument.qVNodeDataStarted = false; - qDocument.qVNodeDataState = undefined; - throw error; + logError(error); + markVNodeDataReady(qDocument); } ); scheduleYieldingIterator(state.$active$); diff --git a/packages/qwik/src/core/client/process-vnode-data.unit.tsx b/packages/qwik/src/core/client/process-vnode-data.unit.tsx index faabf2953f7..bf710792221 100644 --- a/packages/qwik/src/core/client/process-vnode-data.unit.tsx +++ b/packages/qwik/src/core/client/process-vnode-data.unit.tsx @@ -4,6 +4,7 @@ import '../../testing/vdom-diff.unit-util'; import { VNodeDataSeparator, getSegmentVNodeRefId } from '../shared/vnode-data-types'; import { DomContainer, getDomContainer } from './dom-container'; import { + enqueueProcessVNodeDataJob, findVDataSectionEnd, processOutOfOrderSegmentVNodeData, processVNodeData, @@ -1067,6 +1068,24 @@ describe('processVnodeData', () => { }); }); +describe('vnode-data resume errors', () => { + it('a resume error unblocks whenVNodeDataReady instead of hanging silently', async () => { + const document = createDocument() as QDocument; + + // Drive the vnode-data pipeline with a throwing iterator + enqueueProcessVNodeDataJob( + document, + (function* () { + yield; + throw new Error('resume boom'); + })() + ); + + await whenVNodeDataReady(document, () => undefined); + expect(true).toBe(true); + }); +}); + describe('emitVNodeSeparators', () => { it('should encode binary correctly', () => { expect(emitVNodeSeparators(0, 1)).toBe(VNodeDataSeparator.ADVANCE_1_CH);