schollz/croc · error · Error

Sender did not complete the croc PAKE handshake

Error message

Sender did not complete the croc PAKE handshake

What it means

Thrown by the recipient (receiveFiles) when the reply to its 'pake' message is not a well-formed 'pake' control message (wrong type, or missing b/b2 payload). This is the second step of the croc SPAKE2 handshake over the relay control channel; the sender is expected to answer with its PAKE bytes and the 32-byte salt. Any other message means the peer is not a compatible sender or the channel is desynchronized.

Source

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

    callbacks.onStatus?.("Securing channel…");
    const curve = "p256";
    const pake = await wasm().pakeInitWithIdentities(
      textEncoder.encode(passphrase),
      0,
      curve,
      PAKE_PURPOSE_TRANSFER,
      room,
    );
    await sendControl(control, {
      t: "pake",
      v: PAKE_PROTOCOL_VERSION,
      b: pake.bytes,
      b2: textEncoder.encode(curve),
    });
    const peerPake = await receiveControl(control);
    if (peerPake.t !== "pake" || !peerPake.b || !peerPake.b2) {
      throw new Error("Sender did not complete the croc PAKE handshake");
    }
    requirePakeVersion(peerPake.v);
    if (peerPake.b2.byteLength !== PAKE_SALT_SIZE) {
      throw new Error(`Sender provided an invalid ${peerPake.b2.byteLength}-byte PAKE salt`);
    }
    const finished = await wasm().pakeUpdate(pake.handle, peerPake.b);
    const peerKeys = await wasm().derivePeerKeys(
      finished.key,
      peerPake.b2,
      PAKE_PURPOSE_TRANSFER,
      room,
      curve,
      pake.bytes,
      peerPake.b,
    );
    await sendControl(control, {
      t: "pake-confirm",
      v: PAKE_PROTOCOL_VERSION,

View on GitHub (pinned to e25f1bdc04)

Solutions

  1. Ensure exactly one sender and one recipient share the code phrase; two recipients cannot handshake
  2. Check the peer's console/error — a mismatch usually fails symmetrically on the other side with 'Recipient did not start a croc PAKE handshake'
  3. Upgrade both peers to the same client version so handshake message ordering matches
  4. If behind the croc relay protocol, verify the relay implementation forwards the raw handshake bytes unchanged
Defensive patterns

Strategy: try-catch

Type guard

function isPakeMessage(m: { t: string; b?: Uint8Array; b2?: Uint8Array }): boolean {
  return m.t === "pake" && !!m.b && m.b.byteLength > 0 && !!m2check(m.b2);
  function m2check(b2?: Uint8Array) { return !!b2 && b2.byteLength === 32; }
}

Try / catch

catch (e) {
  if (e instanceof Error && e.message === "Sender did not complete the croc PAKE handshake") {
    // peer is not a compatible sender (possibly another recipient): retry with a fresh code phrase
  }
  throw e;
}

Prevention

When it happens

Trigger: The room was joined by another recipient instead of a sender (both sides sent pake and neither responds correctly); sender aborted between connectRelay and its pake reply, and the relay delivered an error/close frame; version-incompatible peer whose first encrypted message decodes to a different type; malformed relay forwarding.

Common situations: Two users both enter receive mode with the same code phrase; one side uses an old client build with a different handshake sequence; relay sends an error message due to room conflicts; testing against a mock sender that does not implement the pake reply.

Understand the failure class

Related errors


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