schollz/croc · error

Stored-transfer manifest is too large

Error message

Stored-transfer manifest is too large

What it means

Thrown when fetching a stored transfer's manifest: the ciphertext returned by GET /{share.id}/manifest exceeds maxManifestCiphertext (256 KiB, stored.ts:17). The manifest lists every file in the transfer; this guard stops the client from feeding an oversized (possibly malicious) payload into wasm storeOpenManifest, which would otherwise allocate proportionally.

Source

Thrown at web/src/protocol/stored.ts:673

    senderMachineID: "encrypted temporary storage",
    noCompress: true,
  };
}

export async function inspectStoredTransfer(
  share: StoredShare,
  settings: StoredSettings,
  signal?: AbortSignal,
) {
  const redeem = await wasm().storeRedeemCapability(share.key);
  const response = await authorizedFetch(
    api(settings, `/${share.id}/manifest`),
    base64URL(redeem),
    { signal },
  );
  const ciphertext = new Uint8Array(await response.arrayBuffer());
  if (ciphertext.byteLength > maxManifestCiphertext) {
    throw new Error("Stored-transfer manifest is too large");
  }
  const plaintext = await wasm().storeOpenManifest(
    share.key,
    share.id,
    ciphertext,
    settings.maxTransferBytes,
  );
  const manifest = JSON.parse(textDecoder.decode(plaintext)) as StoredManifest;
  return {
    share,
    manifest,
    offer: offerFromManifest(manifest),
    expiresAt: response.headers.get("X-Croc-Expires-At") ?? undefined,
  } satisfies StoredInspection;
}

function claimSessionKey(id: string) {
  return `croc-store-claim:${id}`;

View on GitHub (pinned to e25f1bdc04)

Solutions

  1. Reduce file count: zip/archive the directory and store the single archive
  2. Split into several smaller stored transfers
  3. If you fork/operate the client, review whether the 256 KiB cap suits your file counts

Example fix

# before
await uploadStoredFiles({ files: everyFileInNodeModules, settings });

# after
const bundle = await zipDirectory(nodeModulesDir);
await uploadStoredFiles({ files: [bundle], settings });
Defensive patterns

Strategy: validation

Validate before calling

// Rough manifest-size projection: ~130+ bytes per file entry after encryption overhead
const projected = files.reduce((n, f) => n + f.name.length + 128, 0);
if (projected > 200_000) throw new Error('too many files for one stored transfer; archive the directory first');

Try / catch

try { await inspectStoredTransfer(share, settings, signal); } catch (e) { if (e instanceof Error && e.message === 'Stored-transfer manifest is too large') { tellSenderToArchiveAndResend(); return; } throw e; }

Prevention

When it happens

Trigger: Inspecting/receiving a share whose encrypted manifest exceeds 256 KiB: transfers with an enormous number of files (each entry carries name, size, hash, mtime). Can also fire if the service returns a corrupted or attacker-controlled oversized body.

Common situations: Sharing directory trees like node_modules, photo archives, or build output with tens of thousands of files; a misbehaving storage endpoint returning junk.

Related errors


AI-assisted analysis of schollz/croc@e25f1bdc04 (2026-08-15). Data as JSON: /api/errors/a44f8b92d6d626d5. Report an issue: GitHub.