Debugging Qwik Symbol Not Found Errors

A resumable page carries references to code that has not been downloaded yet, and a symbol resolution failure is what happens when one of those references cannot be honoured. The error surfaces at the first interaction a visitor makes, long after the page looked fine, which makes it feel mysterious. It has four causes, all of them deployment-shaped, and all of them cheap to distinguish. The underlying model is in Qwik resumable architecture.

Prerequisites

Diagnostic Steps

Four Causes, One Message Requesting the failing chunk URL directly separates the causes. A 404 where the file exists under another prefix means the base path is wrong. A 404 with no such file anywhere means a deploy removed it. A 200 with the symbol absent means the manifest is stale. A 200 with the symbol present points at a caching layer serving an old document. Request the chunk URL β€” the response identifies the cause 404, and the file exists under a different prefix β†’ base path misconfigured 404, and no such file anywhere β†’ a deploy deleted the chunk 200, but the symbol is not in it β†’ manifest and document are from different builds 200 and the symbol is present β†’ a cache served an old document to a new visitor

Step 1 β€” Capture the symbol and the URL it resolved to

// Log the failure with everything needed to reproduce it, at the point it happens.
window.addEventListener('qerror', (event) => {
  reportError('qwik-symbol', {
    message: String(event.detail?.error || event.detail),
    // The element attribute carries chunk#symbol β€” both halves matter.
    ref: document.activeElement?.getAttribute('on:click') || 'unknown',
    buildId: document.documentElement.dataset.buildId,   // stamp every document
    url: location.pathname,
  });
});

Stamping the document with a build identifier is the single most useful preparation for this class of bug: it turns β€œsome visitors see errors” into β€œvisitors holding build 4f2a see errors”, which names the deploy immediately.

Step 2 β€” Ask the network what exists

# Does the chunk exist at the URL the document expects?
curl -sI https://example.test/build/q-a91f3c2b.js | head -1
# 404 β†’ the file is gone or the prefix is wrong
# 200 β†’ fetch it and look for the symbol
curl -s https://example.test/build/q-a91f3c2b.js | grep -c 's_8Kd2Lp'

Step 3 β€” Compare the document’s build against the deployed manifest

If the document was rendered by one build and the manifest served by another, symbols will reference chunks the manifest does not describe. This happens most often when the server and the static assets deploy independently β€” a rolling update where one instance is new and another is old will produce it intermittently, affecting a fraction of requests that changes as the rollout proceeds.

Step 4 β€” Fix the ordering and the retention

Publish chunks first, switch the document second, and keep previous chunks for a grace period. Because the filenames are content-hashed, retaining them costs storage and nothing else. This ordering is the same one described in edge caching and delivery for islands, and it prevents the largest share of these reports outright.

Verification

A Deploy Ordering That Cannot Break Open Tabs Four steps in order: publish new chunks, verify one of them is reachable, switch the document and manifest together, and retain the previous chunks for a grace period. Because chunk names are content-hashed, the old and new sets coexist and a visitor holding an old document can still resolve every symbol it references. Order matters more than speed 1 Β· publish the new chunks and wait for propagation 2 Β· verify one new chunk is reachable through the CDN, not just the origin 3 Β· switch the document and the manifest together, never separately 4 Β· retain the previous chunks for a grace period β€” hours, not seconds

After changing the deploy process, verify it deliberately rather than waiting for the next incident. Deploy twice in quick succession while holding a page open from the first deploy, then interact with it. Every symbol must resolve, which proves the retention window is real. Then check the error rate for the symbol class over the following day; it should fall to zero rather than to a low number, because the remaining causes are all deployment-shaped and none of them is probabilistic.

Add one permanent guard: a smoke test that loads a page, waits for it to settle, and then triggers an interaction on each interactive element. Because resumability defers code loading until interaction, an ordinary page-load smoke test will not catch a broken symbol reference β€” only interacting does.

Why Resumability Makes Deploys Sharper

Every architecture that defers code loading has this property, and resumability has it most acutely: the document is a promise about code that will be requested later, so the deploy that changes the code has to honour promises made by earlier deploys.

A traditional bundle avoids the issue by loading everything up front β€” by the time a deploy happens, the visitor already holds all the code they will need. An islands page is somewhere in between, because a deferred island fetched after a deploy has the same problem for that one chunk. Resumability turns the exception into the rule: nearly every interaction resolves a reference, so nearly every interaction is exposed to a deploy that removed the target.

The practical consequences are worth stating plainly. Asset retention becomes a correctness requirement rather than a nicety. Deploy ordering matters. Rolling updates need the document and asset layers to move together or to tolerate each other. And error reporting needs a build stamp on every document, because without it the reports are unattributable. None of this is difficult, and all of it is invisible until the first deploy that deletes chunks while people are using the site.

Stamp Every Document With Its Build A build identifier in the served HTML converts an unattributable error rate into a named deploy, which is the difference between a day of investigation and a minute. One attribute makes the reports actionable without a build stamp "some visitors see errors" β€” no way to tie reports to a release with a build stamp "visitors holding build 4f2a see errors" β€” the deploy names itself costs one data attribute on the html element

Troubleshooting

Why does this only happen after a deploy?

Because a resumable document carries references to specific code chunks by name, and those names change when the code changes. A visitor holding a page from the previous deploy still has the old names in their HTML; if the deploy removed those files, the first interaction after the deploy fails. Nothing is wrong with the code β€” the document and the assets simply came from different builds.

Should old chunks be deleted on deploy?

Not immediately. Keep them for at least as long as a visitor might plausibly hold a page open β€” a few hours is a common choice, a day is safer. Because the filenames are content-hashed, old and new coexist without conflict, and the storage cost is trivial next to the failure it prevents. Deleting them at deploy time converts every open tab into a broken page.

How do I tell a stale manifest from a bad base path?

Request the failing chunk URL directly. If it 404s and the same file exists under a different prefix, the base path is wrong. If it 404s and the file does not exist under any prefix, the chunk was removed by a deploy. If it returns 200 but the symbol is still not found, the manifest and the document disagree about which chunk contains that symbol, which is the stale-manifest case.

← Back to Qwik Resumable Architecture