schollz/croc · error · Error

Recipient did not start a croc PAKE handshake

Error message

Recipient did not start a croc PAKE handshake

What it means

On the sender side, after the relay room is joined the first control message from the recipient must be of type 'pake' with both b (PAKE bytes) and b2 (curve name) present. Anything else means the recipient did not begin the croc PAKE handshake as expected, e.g. an error frame, a probe, or a client speaking a different message order.

Source

Thrown at web/src/protocol/client.ts:410

  const { room, passphrase } = await wasm().codeComponents(secret);
  let control: CrocSocket | undefined;
  let data: CrocSocket[] = [];
  let key: Uint8Array | undefined;
  try {
    callbacks.onStatus?.("Connecting to relay…");
    const relay = await connectRelay(
      settings,
      room,
      controlPort(settings.relayAddress),
      signal,
    );
    control = relay.socket;
    callbacks.onStatus?.("Waiting for recipient…");
    await waitForHandshake(control, room, passphrase, signal);

    const peerPake = await receiveControl(control);
    if (peerPake.t !== "pake" || !peerPake.b || !peerPake.b2) {
      throw new Error("Recipient did not start a croc PAKE handshake");
    }
    requirePakeVersion(peerPake.v);
    const curve = textDecoder.decode(peerPake.b2);
    const pake = await wasm().pakeInitWithIdentities(
      textEncoder.encode(passphrase),
      1,
      curve,
      PAKE_PURPOSE_TRANSFER,
      room,
    );
    const finished = await wasm().pakeUpdate(pake.handle, peerPake.b);
    const salt = randomBytes(PAKE_SALT_SIZE);
    const peerKeys = await wasm().derivePeerKeys(
      finished.key,
      salt,
      PAKE_PURPOSE_TRANSFER,
      room,
      curve,

View on GitHub (pinned to e25f1bdc04)

Solutions

  1. Confirm both parties use the same transfer code and compatible croc versions
  2. Retry the transfer end-to-end with a freshly generated code
  3. If it persists, log the actual message type received to identify the divergent peer
Defensive patterns

Strategy: try-catch

Type guard

function isPakeMessage(msg) {
  return !!msg && msg.t === "pake" && msg.b instanceof Uint8Array && msg.b2 instanceof Uint8Array;
}

Try / catch

try {
  await sendFiles(opts);
} catch (e) {
  if (/did not start a croc PAKE handshake/.test(e.message)) {
    showBanner("Recipient could not handshake. Retry with a fresh code.");
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: The recipient being a different croc version that sends another message type first; the recipient sending a relay 'error' or leaving the room so the received frame is not 'pake'; connecting with the wrong code so the peer in the room is a different transfer's participant.

Common situations: Version skew between the two web/CLI clients; code-phrase collisions putting two different transfers in one room; recipient cancelling right at handshake time.

Understand the failure class

Related errors


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