schollz/croc · error · Error
Streaming downloads support SHA-256 verification only
Error message
Streaming downloads support SHA-256 verification only
What it means
Thrown by StreamingDownloadSink.hash() when called with "xxhash". The streaming download sink (service-worker based receive path used when File System Access API is unavailable) hashes data incrementally with SHA-256 as chunks are written, so it physically has no xxhash digest to return. Any verification flow that requests the xxhash algorithm on this sink fails immediately.
Source
Thrown at web/src/protocol/storage.ts:268
if (position !== this.offset) {
throw new Error("Streaming browser downloads require sequential chunks");
}
await wasm().sha256Update(this.hashHandle, bytes);
const copy = Uint8Array.from(bytes);
await this.send({ type: "chunk", bytes: copy.buffer }, [copy.buffer]);
this.offset += bytes.byteLength;
}
async finalize() {
if (this.closed) return;
this.closed = true;
this.digest = await wasm().sha256Final(this.hashHandle);
await this.send({ type: "end" });
}
async hash(algorithm: "xxhash" | "sha256" = "sha256") {
if (algorithm !== "sha256") {
throw new Error("Streaming downloads support SHA-256 verification only");
}
if (!this.digest) throw new Error("Destination must be finalized before hashing");
return this.digest;
}
async commit() {}
async abort() {
if (this.closed) return;
this.closed = true;
await this.send({ type: "abort" }).catch(() => undefined);
}
}
export class StreamingDownloadDestination implements ReceiveDestination {
async createEmptyFolder() {}
async openFile(file: OfferedFile) {View on GitHub (pinned to e25f1bdc04)
Solutions
- Use verifySinkSHA256(sink, expected) / sink.hash("sha256") for streaming destinations
- Branch on destination type before verification: xxhash for Directory/Download sinks, SHA-256 for StreamingDownloadSink
- Have the sender/manifest also advertise SHA-256 (stored transfers already store per-file SHA-256 in manifest field h) so SHA-256 verification is always possible
Example fix
// before
await verifySink(sink, offered.hash); // xxhash -> throws on StreamingDownloadSink
// after
await verifySinkSHA256(sink, expectedSHA256); // matches StreamingDownloadSink.hash("sha256") Defensive patterns
Strategy: type-guard
Validate before calling
import { StreamingDownloadDestination } from "../protocol/storage";
// destination comes from chooseStoredReceiveDestination() Type guard
const isStreaming = (d: ReceiveDestination): boolean => d instanceof StreamingDownloadDestination; // then: isStreaming(d) ? await verifySinkSHA256(sink, sha) : await verifySink(sink, xxhash)
Try / catch
try { await sink.hash("sha256"); } catch (e) { if (e instanceof Error && e.message.includes("SHA-256 verification only")) { /* branch to sha256 */ } throw e; } Prevention
- Always pair StreamingDownloadDestination sinks with verifySinkSHA256, never verifySink
- Require stored manifests to carry SHA-256 (they do: field h) and make SHA-256 the universal verification path
When it happens
Trigger: Calling sink.hash("xxhash") or verifySink(sink, expected) (which defaults to xxhash) on a StreamingDownloadDestination-produced sink. This happens when receive code paths written for DirectorySink/DownloadSink are reused verbatim with StreamingDownloadDestination.
Common situations: Receiving on Firefox/Safari (no showDirectoryPicker) where chooseStoredReceiveDestination returns a StreamingDownloadDestination, then running the legacy xxhash verification step; adding a new destination type without updating the verification branch to use verifySinkSHA256.
Related errors
- The sender advertised xxhash ${hex(expected)}, but the recei
- Hash algorithm "${info.HashAlgorithm}" is not supported
- Streaming browser downloads are unavailable
- Streaming download service did not start
- AbortError
AI-assisted analysis of schollz/croc@e25f1bdc04 (2026-08-15).
Data as JSON: /api/errors/721d1baf3e0f9e90.
Report an issue: GitHub.