{"record":{"id":"6ef7c933b789a0bf","repo":"schollz/croc","slug":"recipient-did-not-secure-the-channel","errorCode":null,"errorMessage":"Recipient did not secure the channel","messagePattern":"Recipient did not secure the channel","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/src/protocol/client.ts","lineNumber":462,"sourceCode":"      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,\n    );\n\n    const peerIP = await receiveControl(control, key);\n    if (peerIP.t !== \"externalip\") throw new Error(\"Recipient did not secure the channel\");\n    await sendControl(control, { t: \"externalip\", m: relay.externalIP }, key);\n    await sendControl(control, {\n      t: \"fileinfo\",\n      b: textEncoder.encode(JSON.stringify(senderInfo(files))),\n    }, key);\n\n    let totalTransferred = 0;\n    for (;;) {\n      checkAbort(signal);\n      const message = await receiveControl(control, key);\n      if (message.t === \"error\") throw new Error(message.m || \"Recipient refused transfer\");\n      if (message.t === \"finished\") {\n        await sendControl(control, { t: \"finished\" }, key);\n        callbacks.onStatus?.(\"Transfer complete\");\n        return;\n      }\n      if (message.t !== \"recipientready\" || !message.b) {\n        throw new Error(`Unexpected peer message: ${message.t}`);","sourceCodeStart":444,"sourceCodeEnd":480,"githubUrl":"https://github.com/schollz/croc/blob/e25f1bdc04f07f094d50b0a1bf67e2563944b57a/web/src/protocol/client.ts#L444-L480","documentation":"After the data connections are opened, the first key-encrypted control message from the recipient must be of type 'externalip' (the peer IP exchange step). Receiving anything else means the channel encryption or message sequencing is off: the peer is not at the expected point in the protocol. Because decryption with the derived key apparently produced a framed message, this usually indicates version/order divergence rather than a bad key.","triggerScenarios":"A recipient that sends a different message first after data-channel setup (older/newer protocol); the control socket delivering an 'error' frame from the recipient; reconnect scenarios where one side restarts the handshake while the other continues.","commonSituations":"Mid-transfer reconnects; clients at different versions disagreeing on post-handshake ordering; recipient hitting an error and emitting an error frame that is read here.","solutions":["Retry with matching croc versions on both ends","Start a completely fresh transfer (new code, new room) rather than resuming half-open state","Log message.t to see which frame arrived instead; an 'error' frame points at the recipient's failure"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isExternalIP(msg) {\n  return !!msg && msg.t === \"externalip\";\n}","tryCatchPattern":"try {\n  await sendFiles(opts);\n} catch (e) {\n  if (/did not secure the channel/.test(e.message)) {\n    showBanner(\"Secure channel could not be established. Restart the transfer.\");\n    return;\n  }\n  throw e;\n}","preventionTips":["Restart transfers end-to-end (new code, new sockets) after any mid-handshake failure","Keep client versions aligned so post-handshake message order matches","Log the unexpected message type to detect divergent peers early"],"tags":["handshake","protocol","encryption","sender"],"backgroundTag":null,"analyzedSha":"e25f1bdc04f07f094d50b0a1bf67e2563944b57a","analyzedAt":"2026-08-15T12:53:39.096Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}