{"record":{"id":"6c858ccb4d252e83","repo":"schollz/croc","slug":"streaming-download-service-did-not-start","errorCode":null,"errorMessage":"Streaming download service did not start","messagePattern":"Streaming download service did not start","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/src/protocol/storage.ts","lineNumber":188,"sourceCode":"\nlet downloadWorker: Promise<ServiceWorker> | undefined;\n\nasync function streamingWorker() {\n  downloadWorker ??= (async () => {\n    if (!(\"serviceWorker\" in navigator) || typeof MessageChannel === \"undefined\") {\n      throw new Error(\"Streaming browser downloads are unavailable\");\n    }\n    const registration = await navigator.serviceWorker.register(\n      `${import.meta.env.BASE_URL}croc-download-sw.js`,\n      { scope: import.meta.env.BASE_URL },\n    );\n    await navigator.serviceWorker.ready;\n    const worker =\n      navigator.serviceWorker.controller ??\n      registration.active ??\n      registration.waiting ??\n      registration.installing;\n    if (!worker) throw new Error(\"Streaming download service did not start\");\n    return worker;\n  })();\n  return downloadWorker;\n}\n\nclass StreamingDownloadSink implements ReceiveSink {\n  private offset = 0;\n  private closed = false;\n  private digest?: Uint8Array;\n  private pending?: {\n    resolve(): void;\n    reject(error: Error): void;\n  };\n\n  private constructor(\n    private port: MessagePort,\n    private hashHandle: number,\n  ) {","sourceCodeStart":170,"sourceCodeEnd":206,"githubUrl":"https://github.com/schollz/croc/blob/e25f1bdc04f07f094d50b0a1bf67e2563944b57a/web/src/protocol/storage.ts#L170-L206","documentation":"After registering croc-download-sw.js and awaiting serviceWorker.ready, streamingWorker() tries controller ?? active ?? waiting ?? installing to obtain a worker handle; if all are null it throws. This means registration nominally succeeded but no worker instance ever materialized — the script failed to install, was terminated, or was blocked.","triggerScenarios":"The service worker script fails to load/evaluate (404 on croc-download-sw.js at BASE_URL, syntax error, wrong MIME type), install is rejected (e.g. an install-event failure), a scope mismatch leaves no worker for the page's scope, or the browser blocks the worker (certain privacy modes, extensions, enterprise policy).","commonSituations":"Build/asset pipeline not emitting croc-download-sw.js or placing it outside import.meta.env.BASE_URL (common with sub-path deployments); dev server missing the file or serving it with an HTML 404 fallback; strict MIME checks rejecting the script; private-browsing modes where registration resolves but no worker activates.","solutions":["Verify croc-download-sw.js is deployed at BASE_URL and served with Content-Type text/javascript — open the URL directly in the browser and check for a 200.","Ensure the SW registration scope (BASE_URL) actually covers the page URL; with sub-path hosting, set BASE_URL correctly at build time.","Re-check after a hard reload; a previously-failed install can leave registration in a bad state — unregister via navigator.serviceWorker.getRegistrations() then reload.","Check the browser console for the service worker's own install/evaluate error and fix that first."],"exampleFix":"# before: SW served at /assets/croc-download-sw.js but BASE_URL=/app/\n# after: emit/copy the worker to /app/croc-download-sw.js (matches BASE_URL scope)","handlingStrategy":"retry","validationCode":"async function downloadWorkerHealthy(): Promise<boolean> {\n  if (!(\"serviceWorker\" in navigator)) return false;\n  try {\n    const reg = await navigator.serviceWorker.getRegistration(\n      `${import.meta.env.BASE_URL}croc-download-sw.js`,\n    );\n    return Boolean(reg?.active || reg?.installing || reg?.waiting);\n  } catch {\n    return false;\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  const worker = await streamingWorker();\n} catch (error) {\n  if (error instanceof Error && error.message === \"Streaming download service did not start\") {\n    downloadWorker = undefined; // clear the memoized failed promise\n    for (const reg of await navigator.serviceWorker.getRegistrations()) await reg.unregister();\n    location.reload(); // one clean retry after unregister; then surface the error\n    return;\n  }\n  throw error;\n}","preventionTips":["Deploy croc-download-sw.js exactly at import.meta.env.BASE_URL with a JavaScript MIME type.","Watch the browser console for service-worker install errors during CI smoke tests on the deployed build.","Clear the memoized downloadWorker promise on failure so a fixed deployment can be retried without a stale rejected promise."],"tags":["service-worker","deployment","download","environment"],"backgroundTag":null,"analyzedSha":"e25f1bdc04f07f094d50b0a1bf67e2563944b57a","analyzedAt":"2026-08-15T12:53:39.096Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}