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.
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
- AbortError
- Hash algorithm " " is not supported
- Streaming browser downloads are unavailable
- Streaming download service did not start
AI-assisted analysis of schollz/croc@e25f1bdc04 (2026-08-15).
Data as JSON: /api/errors/721d1baf3e0f9e90.
Report an issue: GitHub.
Appendix: 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)