schollz/croc · error · Error

Invalid stored-transfer key

Error message

Invalid stored-transfer key

What it means

validateShare() requires the share key to be exactly 32 bytes (storedKeyBytes). The key comes from base64url-decoding the URL fragment (#v1....) or the token's last segment; a decode that yields any other length — from truncation, padding characters, or unicode mangling — is rejected before any decryption is attempted with it.

Source

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

  if (
    (parsed.protocol !== "https:" && !(parsed.protocol === "http:" && loopback)) ||
    parsed.username ||
    parsed.password ||
    (parsed.pathname !== "/" && parsed.pathname !== "") ||
    parsed.search ||
    parsed.hash
  ) {
    throw new Error("Stored-transfer origin must contain only an HTTPS scheme and host");
  }
  return parsed.origin;
}

function validateShare(share: StoredShare) {
  if (!/^[A-Za-z0-9_-]{22}$/.test(share.id)) {
    throw new Error("Invalid stored-transfer id");
  }
  if (share.key.byteLength !== storedKeyBytes) {
    throw new Error("Invalid stored-transfer key");
  }
  share.origin = normalizeOrigin(share.origin);
  return share;
}

export function formatStoredBrowserURL(share: StoredShare) {
  validateShare(share);
  return `${share.origin}/s/${share.id}#v1.${base64URL(share.key)}`;
}

export function formatStoredCLIToken(share: StoredShare) {
  validateShare(share);
  return [
    storedProtocol,
    base64URL(textEncoder.encode(share.origin)),
    share.id,
    base64URL(share.key),
  ].join(".");

View on GitHub (pinned to e25f1bdc04)

Solutions

  1. Copy the entire share URL including the #v1.<key> fragment; many chat clients require explicit 'copy link'
  2. If generating URLs yourself, base64URL-encode exactly the 32-byte key with no padding
  3. Pre-validate with a regex on the fragment length (43 unpadded base64url chars encode 32 bytes) before calling parseStoredShare

Example fix

// validation before parse
const keyPart = url.hash.slice(4);
if (!/^[A-Za-z0-9_-]{43}$/.test(keyPart)) throw new Error("Share link is missing its key fragment");
Defensive patterns

Strategy: validation

Validate before calling

const isValidShareKey = (key: Uint8Array): boolean => key.byteLength === 32;
// for the URL fragment: /^[A-Za-z0-9_-]{43}$/.test(hash.slice(4))

Prevention

When it happens

Trigger: parseStoredShare where fromBase64URL(parsed.hash.slice(4)) or parts[3] decodes to != 32 bytes; also createStoredUpload if wasm().storeGenerateKey() ever returned a different length (it does not in a consistent build).

Common situations: Share links mangled by messengers that strip or truncate the fragment; users copying only part of the URL and dropping the #v1. key fragment (usually caught as invalid URL first); mixing client versions where the key format changed.

Related errors


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