{"record":{"id":"a3eb7d181dfeea17","repo":"schollz/croc","slug":"recipient-did-not-confirm-the-croc-pake-handshake","errorCode":null,"errorMessage":"Recipient did not confirm the croc PAKE handshake","messagePattern":"Recipient did not confirm the croc PAKE handshake","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/src/protocol/client.ts","lineNumber":440,"sourceCode":"    const salt = randomBytes(PAKE_SALT_SIZE);\n    const peerKeys = await wasm().derivePeerKeys(\n      finished.key,\n      salt,\n      PAKE_PURPOSE_TRANSFER,\n      room,\n      curve,\n      peerPake.b,\n      finished.bytes,\n    );\n    await sendControl(control, {\n      t: \"pake\",\n      v: PAKE_PROTOCOL_VERSION,\n      b: finished.bytes,\n      b2: salt,\n    });\n    const confirmationA = await receiveControl(control);\n    if (confirmationA.t !== \"pake-confirm\" || !confirmationA.b) {\n      throw new Error(\"Recipient did not confirm the croc PAKE handshake\");\n    }\n    requirePakeVersion(confirmationA.v);\n    if (!(await wasm().confirmPeerKey(peerKeys.confirmationA, confirmationA.b))) {\n      throw new Error(\"Recipient PAKE confirmation failed\");\n    }\n    await sendControl(control, {\n      t: \"pake-confirm\",\n      v: PAKE_PROTOCOL_VERSION,\n      b: peerKeys.confirmationB,\n    });\n    key = peerKeys.key;\n\n    callbacks.onStatus?.(\"Opening encrypted data channels…\");\n    data = await openDataConnections(\n      settings,\n      room,\n      dataPorts(relay.banner),\n      signal,","sourceCodeStart":422,"sourceCodeEnd":458,"githubUrl":"https://github.com/schollz/croc/blob/e25f1bdc04f07f094d50b0a1bf67e2563944b57a/web/src/protocol/client.ts#L422-L458","documentation":"After the sender posts its PAKE response, the recipient must reply with a 'pake-confirm' message carrying confirmation bytes (b). If the message type differs or b is missing, the handshake confirmation step never happened. This sits between the PAKE exchange and the key-confirmation check, so it fires on message-order or framing problems rather than wrong-passphrase failures.","triggerScenarios":"The recipient aborting or erroring between its PAKE message and the confirmation; a version that skips or renames 'pake-confirm'; socket desynchronization from earlier partial reads.","commonSituations":"Recipient closes the tab mid-handshake; recipient on an older protocol without the confirm step; relay delivering frames out of order after a reconnect.","solutions":["Retry the transfer with both ends on matching croc versions","Ensure the recipient keeps the connection open through the full handshake (no early cancel)","Regenerate the code and start a fresh room"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isPakeConfirm(msg) {\n  return !!msg && msg.t === \"pake-confirm\" && msg.b instanceof Uint8Array && msg.b.length > 0;\n}","tryCatchPattern":"try {\n  await sendFiles(opts);\n} catch (e) {\n  if (/did not confirm/.test(e.message)) { reconnectAndRetryOnce(); return; }\n  throw e;\n}","preventionTips":["Keep the recipient connection open through the whole handshake (no early cancel)","On reconnect, build a fresh room and sockets rather than resuming","Match protocol versions on both ends"],"tags":["pake","handshake","confirmation","sender"],"backgroundTag":null,"analyzedSha":"e25f1bdc04f07f094d50b0a1bf67e2563944b57a","analyzedAt":"2026-08-15T12:53:39.096Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}