schollz/croc · error · Error
Relay could not open the room: ${confirmation}
Error message
Relay could not open the room: ${confirmation} What it means
After relay authentication the client sends the room identifier (derived from the transfer code) and expects the encrypted reply 'ok'. Any other confirmation string means the relay could not join or create that room, and the string is included in the message (commonly 'room full' or a similar reason). This guards the room-based rendezvous step before any peer communication starts.
Source
Thrown at web/src/protocol/client.ts:131
const peer = await socket.receive();
const finished = await engine.pakeUpdate(pake.handle, peer);
const salt = randomBytes(8);
const key = await engine.deriveKey(finished.key, salt);
await socket.send(salt);
await socket.send(await engine.encrypt(textEncoder.encode(settings.relayPassword), key));
const response = textDecoder.decode(
await engine.decrypt(await socket.receive(), key),
);
const separator = response.indexOf("|||");
if (separator < 0) throw new Error(`Relay rejected the connection: ${response}`);
const banner = response.slice(0, separator);
const externalIP = response.slice(separator + 3);
await socket.send(await engine.encrypt(textEncoder.encode(room), key));
const confirmation = textDecoder.decode(
await engine.decrypt(await socket.receive(), key),
);
if (confirmation !== "ok") {
throw new Error(`Relay could not open the room: ${confirmation}`);
}
return { socket, banner, externalIP } satisfies RelayConnection;
} catch (error) {
socket.close();
throw error;
}
}
async function sendControl(
socket: CrocSocket,
message: CrocMessage,
key?: Uint8Array,
) {
await socket.send(await encodeMessage(wasm(), message, key));
}
async function receiveControl(socket: CrocSocket, key?: Uint8Array) {
return decodeMessage(wasm(), await socket.receive(), key);View on GitHub (pinned to e25f1bdc04)
Solutions
- Read the confirmation string in the message for the relay's stated reason
- Use a fresh code phrase (regenerate rather than reusing a custom one) and retry
- Wait a few seconds for the relay to reap the stale room, then reconnect
Example fix
// before: reusing the same custom room
await sendFiles({ files, secret: "project-files", settings }); // room occupied
// after: unique code per transfer
const secret = await newCodePhrase(); // e.g. 'baker-glass-iron-leaf'
await sendFiles({ files, secret, settings }); Defensive patterns
Strategy: retry
Try / catch
try {
await sendFiles(opts);
} catch (e) {
if (/could not open the room/.test(e.message) && attemptsLeft--) {
await delay(3000); // let the relay reap the stale room
opts.secret = await newCode(); // fresh room
return sendFiles(opts);
}
throw e;
} Prevention
- Use unique generated codes per transfer instead of fixed custom codes
- Wait a few seconds before retrying the same code after an aborted transfer
- Avoid three parties sharing one code phrase simultaneously
When it happens
Trigger: Three or more parties using the same code phrase so the room already has its two participants; relay limits on concurrent rooms; a relay whose room bookkeeping rejected the join.
Common situations: Reusing a short custom code while a previous transfer with the same code is still connected; retrying immediately after an aborted transfer before the relay times the old room out.
Related errors
- Peer uses unsupported PAKE protocol version ${version ?? 0};
- Relay returned an invalid port list: ${banner}
- Relay rejected the connection: ${response}
- Peer sent an unexpected control handshake
- Recipient did not start a croc PAKE handshake
AI-assisted analysis of schollz/croc@e25f1bdc04 (2026-08-15).
Data as JSON: /api/errors/ede996b4441ad421.
Report an issue: GitHub.