Skip to content

[issue-163] §5.4: offline claim fails on first use — libdedx WASM + ASR worker never precached #217

Description

@grzanka

Summary

Verified issue #163 §5.4 ("Verify the offline claim") via live browser testing against the deployed
GitHub Pages instance (https://aptg.github.io/aidedx/, Firefox on Linux, 2026-08-13). Confirmed:
the offline claim fails on first use
, even after completing the "download models" consent flow —
but succeeds once the app has completed one successful query while online, within the same tab
session.

Repro

  1. Open https://aptg.github.io/aidedx/ in Firefox.
  2. Go through the "download models" consent flow in the UI and let it finish completely.
  3. In DevTools → Storage tab → Cache Storage: confirm the Whisper model weight files are cached (a
    transformers-cache bucket appears with .onnx/tokenizer entries).
  4. Disconnect networking at the OS level (airplane mode, or nmcli networking off on Linux) — not
    just DevTools' network throttling.
  5. Without reloading the page, submit a real query (e.g. "range of 40 MeV protons in water").
  6. Console shows:
    Failed to load 'https://aptg.github.io/aidedx/wasm/libdedx.mjs'. A ServiceWorker passed a
    promise to FetchEvent.respondWith() that rejected with 'TypeError: NetworkError when attempting
    to fetch resource.'  coi-serviceworker.js:37:11
    
    Loading failed for the module with source
    "https://aptg.github.io/aidedx/wasm/libdedx.mjs".
    
    Failed to load 'https://aptg.github.io/aidedx/_app/immutable/workers/asr.worker-CB3MXUgI.js'.
    A ServiceWorker passed a promise to FetchEvent.respondWith() that rejected with 'TypeError:
    NetworkError when attempting to fetch resource.'  coi-serviceworker.js:37:11
    
    Failed to load libdedx WASM module: error loading dynamically imported module:
    https://aptg.github.io/aidedx/wasm/libdedx.mjs
    
    The UI gets stuck at "Warming up… Ns" indefinitely — the ASR worker that would complete that
    step never loads.
  7. Reconnect networking, reload once, and let one query run to completion online.
  8. Re-disconnect networking (no reload this time) and submit another query — this time it
    succeeds.

Root cause

  • src/lib/wasm/loader.ts's loadService() dynamic-imports wasm/libdedx.mjs lazily, only when
    a query first needs a computed number
    — deliberate, per its own doc comment (keeps WASM out of
    the initial bundle).
  • src/lib/asr/asr-status.svelte.ts's #getWorkerClient() similarly constructs the ASR Worker
    (src/lib/asr/worker-client.ts:24, new Worker(new URL("./asr.worker.ts", ...))) lazily, only on
    first use.
  • src/lib/models/download.ts's "download models" consent flow only fetches Hugging Face model
    weights via transformers.js's from_pretrained(). It never touches wasm/libdedx.mjs or the ASR
    worker script, so neither gets fetched — and therefore neither gets cached anywhere — until a
    query actually runs.
  • Compounding it: static/coi-serviceworker.js's fetch handler has no cache fallback at all:
    event.respondWith(
      fetch(request)
        .then((response) => { ... })
        .catch((e) => {
          console.error(e);
          throw e;   // static/coi-serviceworker.js:59 (the .catch block starting at line 54)
        }),
    );
    So even a resource with a stale-but-valid browser HTTP cache entry can't be served through the
    service worker once its live fetch fails outright. This is a separate, secondary gap from the
    "never fetched" root cause above — it also explains an earlier hard-reload (Ctrl+Shift+R) failure
    of the page shell itself observed during this same testing session, independent of any
    model/worker caching: a hard reload deliberately bypasses all caching for the navigation request,
    and this service worker's no-fallback behavior guarantees that fails when offline regardless of
    what's cached.
  • The ORT-wasm-from-jsdelivr CDN dependency — the original hypothesis in [audit] Bug hunt + architecture/maintainability review: 22 bugs (B1-B7 fixed; 2 regressions + 10 new found on re-audit), cross-layer contract drift as the root pattern, libdedx forward-compat, and a docs/benchmark-corpus reorganization plan #163 §5.4 — was never
    actually reached in this repro; the app fails earlier, on its own same-origin code, before that
    CDN resource is ever requested. Separately confirmed by reading
    node_modules/@huggingface/transformers/dist/transformers.js: env.useWasmCache defaults to
    true in a browser and pre-caches the ORT wasm/mjs files into the same Cache Storage bucket
    (env.cacheKey, default "transformers-cache") as model weights, the first time an inference
    session is built. That appears to work correctly once actually exercised, consistent with the
    "works after first use" result above — but this specific piece hasn't been isolated/confirmed by
    itself, since the two earlier same-origin failures blocked reaching it directly.

Suggested fix

  1. Precache the compute/ASR code paths as part of the download flow. After model weights finish
    downloading, download.ts (or a caller right after it) should also invoke getService() and
    construct the ASR worker once, so a user who completes "download models" is actually ready for
    offline use — without needing to stumble through one online query first. (This was the
    reporter's own suggestion.)
  2. Give coi-serviceworker.js's fetch handler a caches.match() fallback on a failed live
    fetch, so a resource that is cached (via fix 1, or ordinary browser HTTP caching) can still be
    served when the live fetch fails outright. Without this, fix 1 alone still depends on the
    browser's ordinary HTTP cache staying valid rather than the more durable Cache Storage.

Test plan

  • Repeat the repro above after the fix: download models → disconnect networking → no prior
    online query
    → submit a query → should succeed immediately.
  • Re-verify the literal §5.4 scenario ("download models, go offline, hard-reload, transcribe") once
    fix 2 lands, since that's the scenario the service-worker fallback specifically targets.
  • Isolate the ORT-wasm-from-jsdelivr question directly (e.g. inspect Cache Storage for
    ort-wasm-simd-threaded* entries after a successful session build) now that the two earlier
    same-origin blockers are understood, to close out that part of §5.4 explicitly rather than by
    inference.

Cross-reference

Verifies/refines #163 §5.4 ("Verify the offline claim"). That item speculated the
failure would be the ORT-wasm-from-jsdelivr CDN dependency; live testing found a more fundamental,
earlier failure in the app's own lazy-loaded code paths instead, with the ORT-wasm question itself
still open (not yet directly isolated).

Found via live browser testing (Firefox, Linux) against the deployed GitHub Pages instance, per
§5.4's own "verification item, not a finding" framing in #163.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions