schollz/croc · error · Error
Sender did not secure the channel
Error message
Sender did not secure the channel
What it means
Thrown by the recipient when the first message received after key establishment (sent encrypted with the derived key) is not 'externalip'. Both peers exchange external IP announcements as the first encrypted control messages to confirm the channel works end-to-end; receiving anything else — typically a decrypt failure artifact, an error frame, or a desynchronized peer — means the encrypted control channel is not functioning as expected.
Source
Thrown at web/src/protocol/client.ts:728
}
requirePakeVersion(confirmationB.v);
if (!(await wasm().confirmPeerKey(peerKeys.confirmationB, confirmationB.b))) {
throw new Error("Sender PAKE confirmation failed");
}
key = peerKeys.key;
data = await openDataConnections(
settings,
room,
dataPorts(relay.banner),
signal,
);
await sendControl(control, {
t: "externalip",
m: relay.externalIP,
b: peerPake.b,
}, key);
const peerIP = await receiveControl(control, key);
if (peerIP.t !== "externalip") throw new Error("Sender did not secure the channel");
const fileInfo = await receiveControl(control, key);
if (fileInfo.t === "error") throw new Error(fileInfo.m || "Sender cancelled");
if (fileInfo.t !== "fileinfo" || !fileInfo.b) {
throw new Error("Sender did not provide file metadata");
}
const sender = JSON.parse(textDecoder.decode(fileInfo.b)) as SenderInfoWire;
const offer = validateSenderInfo(sender);
callbacks.onStatus?.("Review the incoming files");
const destination = await callbacks.onOffer(offer);
if (!destination) {
await sendControl(control, { t: "error", m: "refusing files" }, key);
throw new Error("Transfer refused");
}
for (const folder of offer.emptyFolders) {
await destination.createEmptyFolder(folder);
}View on GitHub (pinned to e25f1bdc04)
Solutions
- Check the sender side — a data-channel setup failure is the usual cause and the peer error tells you which port/relay failed
- Verify the relay banner port list is valid and those ports are reachable by both peers
- Upgrade both peers so the post-handshake message order (externalip <-> externalip) matches
- Retry with a different relay to rule out relay-level frame injection or connection resets
Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-flight: verify the relay's data ports are parseable and reachable before handshake
const ports = dataPorts(relay.banner);
if (ports.length === 0) throw new Error("relay banner has no data ports"); Type guard
function isExternalIP(m: { t: string }): boolean { return m.t === "externalip"; } Try / catch
catch (e) {
if (e instanceof Error && e.message === "Sender did not secure the channel") {
// peer failed post-handshake (usually data-channel setup): check peer logs / relay ports, retry
}
throw e;
} Prevention
- Confirm relay data ports are open in both peers' firewalls before starting
- Coordinate cancel UX so one peer aborting mid-setup does not surprise the other
- Retry the whole transfer on this error; partial resume is not supported
When it happens
Trigger: Peer sends an 'error' control message instead (it aborted during data-connection setup); decrypt fails or produces a message whose type decodes to something unexpected because keys differ (should have failed at pake-confirm, but partial divergence is possible); relay injects a banner or close frame; peer implementation skips the externalip exchange.
Common situations: Sender fails at openDataConnections (relay port list invalid, data ports unreachable) and reports an error; version-skewed peer with a different post-handshake choreography; the control socket receives a relay-level close frame mistaken for a peer message.
Related errors
- Peer sent an unexpected control handshake
- Recipient did not start a croc PAKE handshake
- Sender did not complete the croc PAKE handshake
- Sender did not confirm the croc PAKE handshake
- Peer uses unsupported PAKE protocol version ${version ?? 0};
AI-assisted analysis of schollz/croc@e25f1bdc04 (2026-08-15).
Data as JSON: /api/errors/6fe1cdcba167e7f7.
Report an issue: GitHub.