schollz/croc · error · Error
Streaming download service did not start
Error message
Streaming download service did not start
What it means
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.
Source
Thrown at web/src/protocol/storage.ts:188
let downloadWorker: Promise<ServiceWorker> | undefined;
async function streamingWorker() {
downloadWorker ??= (async () => {
if (!("serviceWorker" in navigator) || typeof MessageChannel === "undefined") {
throw new Error("Streaming browser downloads are unavailable");
}
const registration = await navigator.serviceWorker.register(
`${import.meta.env.BASE_URL}croc-download-sw.js`,
{ scope: import.meta.env.BASE_URL },
);
await navigator.serviceWorker.ready;
const worker =
navigator.serviceWorker.controller ??
registration.active ??
registration.waiting ??
registration.installing;
if (!worker) throw new Error("Streaming download service did not start");
return worker;
})();
return downloadWorker;
}
class StreamingDownloadSink implements ReceiveSink {
private offset = 0;
private closed = false;
private digest?: Uint8Array;
private pending?: {
resolve(): void;
reject(error: Error): void;
};
private constructor(
private port: MessagePort,
private hashHandle: number,
) {View on GitHub (pinned to e25f1bdc04)
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.
Example fix
# before: SW served at /assets/croc-download-sw.js but BASE_URL=/app/ # after: emit/copy the worker to /app/croc-download-sw.js (matches BASE_URL scope)
Defensive patterns
Strategy: retry
Validate before calling
async function downloadWorkerHealthy(): Promise<boolean> {
if (!("serviceWorker" in navigator)) return false;
try {
const reg = await navigator.serviceWorker.getRegistration(
`${import.meta.env.BASE_URL}croc-download-sw.js`,
);
return Boolean(reg?.active || reg?.installing || reg?.waiting);
} catch {
return false;
}
} Try / catch
try {
const worker = await streamingWorker();
} catch (error) {
if (error instanceof Error && error.message === "Streaming download service did not start") {
downloadWorker = undefined; // clear the memoized failed promise
for (const reg of await navigator.serviceWorker.getRegistrations()) await reg.unregister();
location.reload(); // one clean retry after unregister; then surface the error
return;
}
throw error;
} Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- Streaming browser downloads are unavailable
- Could not load croc.wasm (${response.status})
- Streaming downloads support SHA-256 verification only
- Stored-transfer manifest is too large
- Storage service returned an invalid claim capability
AI-assisted analysis of schollz/croc@e25f1bdc04 (2026-08-15).
Data as JSON: /api/errors/6c858ccb4d252e83.
Report an issue: GitHub.