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
- Ensure exactly one sender and one recipient share the code phrase; two recipients cannot handshake
- Check the peer's console/error — a mismatch usually fails symmetrically on the other side with 'Recipient did not start a croc PAKE handshake'
- Upgrade both peers to the same client version so handshake message ordering matches
- 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
- Ensure exactly one sender and one recipient share a code phrase; coordinate roles before starting
- Use long random code phrases so rooms cannot collide
- On handshake-type errors, always retry with a NEW code phrase rather than rejoining the same room
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
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- Peer sent an unexpected control handshake
- Recipient did not start a croc PAKE handshake
- Recipient PAKE confirmation failed
- Sender provided an invalid ${peerPake.b2.byteLength}-byte PA
- Sender did not confirm the croc PAKE handshake
AI-assisted analysis of schollz/croc@e25f1bdc04 (2026-08-15).
Data as JSON: /api/errors/11740187e1fe237a.
Report an issue: GitHub.