schollz/croc · error · Error
Streaming browser downloads are unavailable
Error message
Streaming browser downloads are unavailable
What it means
streamingWorker() lazily registers the download service worker that enables streamed file writes; it throws immediately when navigator.serviceWorker or MessageChannel is unavailable. Without those APIs the browser cannot intercept download URLs or pump chunks cross-context, so streaming downloads cannot be offered at all.
Source
Thrown at web/src/protocol/storage.ts:176
async commit() {
if (!this.blob) throw new Error("Destination must be finalized before download");
if (this.committed) return;
this.committed = true;
this.onDownload(this.name, this.blob);
}
async abort() {
this.chunks.clear();
this.blob = undefined;
}
}
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 {View on GitHub (pinned to e25f1bdc04)
Solutions
- Serve the app from a secure context: https://, or localhost/127.0.0.1 during development.
- Feature-detect before choosing the sink: fall back to an in-memory/blob or File System Access API sink when 'serviceWorker' in navigator is false.
- For webview embedding, enable service worker support in the native shell (Android WebView: enable ServiceWorker via ServiceWorkerController, iOS WKWebView supports it in secure contexts).
- In tests, stub navigator.serviceWorker and MessageChannel or skip the streaming path.
Example fix
// before const sink = new StreamingDownloadSink(...); // throws on http:// LAN host // after const canStream = "serviceWorker" in navigator && typeof MessageChannel !== "undefined"; const sink = canStream ? new StreamingDownloadSink(...) : new MemoryBlobSink(...);
Defensive patterns
Strategy: fallback
Validate before calling
function streamingDownloadsSupported(): boolean {
return typeof navigator !== "undefined" &&
"serviceWorker" in navigator &&
typeof MessageChannel !== "undefined" &&
window.isSecureContext;
} Try / catch
try {
sink = new StreamingDownloadSink(await streamingWorker());
} catch (error) {
if (error instanceof Error && error.message === "Streaming browser downloads are unavailable") {
sink = new MemoryBlobSink(); // degrade to buffered download with a size warning
return;
}
throw error;
} Prevention
- Serve the app over https or localhost — service workers require a secure context.
- Feature-detect serviceWorker + MessageChannel before offering the streaming path; provide a blob/memory fallback.
- Test in the actual minimum browser matrix, including webviews and private modes.
When it happens
Trigger: Calling into the streaming download path (StreamingDownloadSink / streamingWorker) in a context lacking ServiceWorker support: non-secure origins (plain http on a non-localhost host), old/embedded browsers/webviews (some Android WebViews, in-app browsers), worker/sandboxed iframe contexts, or where MessageChannel is missing.
Common situations: Deploying the web client over http:// on a LAN IP (service workers require a secure context); iOS in-app browsers and other webviews; serving from file://; corporate-embedded browsers; testing in jsdom which lacks real serviceWorker registration.
Related errors
- Streaming download service did not start
- Streaming downloads support SHA-256 verification only
- Stored-transfer manifest is too large
- Storage service returned an invalid claim capability
- Storage service returned an invalid remaining-download count
AI-assisted analysis of schollz/croc@e25f1bdc04 (2026-08-15).
Data as JSON: /api/errors/b10f283d5fe48c1a.
Report an issue: GitHub.