schollz/croc · error

Stored transfer exceeds the

Error message

Stored transfer exceeds the ${settings.maxTransferBytes} byte limit

What it means

prepareStoredFiles() sums all selected file sizes and rejects the batch if the total is not a safe integer or exceeds settings.maxTransferBytes. Storage reservations are sized up front, so oversize batches are stopped before any hashing work is done.

Solutions

  1. Reduce the selection or split into several stored transfers under the byte limit
  2. Pre-compute the total in the UI and show remaining allowance before submit
  3. Verify the maxTransferBytes value the client actually received from settings — it may be lower than the server's real capacity

Example fix

// before
const prepared = await prepareStoredFiles(selected, settings);

// after
const total = selected.reduce((s, f) => s + f.size, 0);
if (total > settings.maxTransferBytes) { alert("Transfer too large; remove some files"); return; }
const prepared = await prepareStoredFiles(selected, settings);
Defensive patterns

Strategy: validation

Validate before calling

const total = selected.reduce((sum, f) => sum + f.size, 0);
if (!Number.isSafeInteger(total) || total > settings.maxTransferBytes) { showError("Selection exceeds the stored-transfer size limit"); return; }

Prevention

When it happens

Trigger: Sum of file.size over maxTransferBytes (e.g., 2 GB selected against a 1 GB cap), or a pathological selection whose total overflows 2^53 (not realistic in browsers, hence the Number.isSafeInteger belt-and-braces).

Common situations: Selecting large media archives; settings served from the server with a lower maxTransferBytes than users expect; disk-size confusion between GB and GiB when the cap is expressed in plain bytes.

Related errors


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

Appendix: source

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

    return await engine.sha256Final(handle);
  } finally {
    reader.releaseLock();
  }
}

export async function prepareStoredFiles(
  selected: File[],
  settings: StoredSettings,
  callbacks: { onStatus?(status: string): void } = {},
  signal?: AbortSignal,
) {
  if (selected.length === 0) throw new Error("Choose at least one file");
  if (selected.length > settings.maxFiles) {
    throw new Error(`Stored transfers can contain at most ${settings.maxFiles} files`);
  }
  const total = selected.reduce((sum, file) => sum + file.size, 0);
  if (!Number.isSafeInteger(total) || total > settings.maxTransferBytes) {
    throw new Error(`Stored transfer exceeds the ${settings.maxTransferBytes} byte limit`);
  }
  const names = new Set<string>();
  const prepared: StoredPreparedFile[] = [];
  let firstChunk = 0;
  for (let index = 0; index < selected.length; index += 1) {
    const file = selected[index];
    const name = normalizeOutgoingFileName(file.name);
    if (names.has(name)) throw new Error(`Duplicate filename: ${name}`);
    names.add(name);
    callbacks.onStatus?.(`Hashing ${index + 1}/${selected.length}: ${name}`);
    const chunkCount = Math.ceil(file.size / storedChunkSize);
    prepared.push({
      file,
      name,
      size: file.size,
      hash: new Uint8Array(),
      sha256: await sha256Blob(file, signal),
      modified: new Date(file.lastModified).toISOString(),

View on GitHub (pinned to e25f1bdc04)