Errors and retries
Tell apart errors to fix from errors to retry, and back off correctly.
Errors reach you in two ways: createRoom() rejects when a room cannot open, and a running room reports problems to onError and to the promises of the commands that failed.
When a room can't open
| Error | Cause | Retry? |
|---|---|---|
TypeError: A valid API key is required. | Missing or malformed key | No: fix the configuration |
Error: Unknown room setting: … and other setting errors | Invalid createRoom() settings | No: fix the code |
RoomAdmissionError with status 400, 401, 403 | Settings rejected, key invalid, or key lacks permission | No |
RoomAdmissionError with status 409 or 410 | The request went stale | Yes, as a new createRoom() call |
RoomAdmissionError with status 429 | A room limit or request budget | Yes, after retryAfterSeconds |
RoomAdmissionError with status 503 | The service is temporarily unavailable | Yes, with backoff |
TimeoutError | Startup took longer than 15 seconds | Yes, with backoff |
Signaling timed out, Host authority was not granted | Signaling failed during startup | Yes, with backoff |
The errors reference lists the status codes in detail.
Back off
Retry only errors that can succeed later, wait longer after each failure, and honour retryAfterSeconds:
open-room.mjs
import { createRoom, RoomAdmissionError } from 'ball2d/node';
const permanent = new Set([400, 401, 403]);
export async function openRoom(config) {
for (let attempt = 0; ; attempt++) {
try {
return await createRoom(config);
} catch (error) {
if (error instanceof TypeError) throw error;
if (
error instanceof RoomAdmissionError &&
permanent.has(error.status)
)
throw error;
const backoff =
Math.min(60, 2 ** attempt) + Math.random();
const wait = error.retryAfterSeconds ?? backoff;
console.warn(
`Room did not open (${error.message});` +
` retrying in ${Math.round(wait)} s`,
);
await new Promise((resolve) =>
setTimeout(resolve, wait * 1000),
);
}
}
}While a room runs
- Failed commands reject their promise, and the room also reports them to
onError. Handle the promise where the failure matters, such as a ban the service didn't confirm. - Handler errors never crash your process. They are reported to
onErroras<event>: <error>. - The room ending is reported to
onErrorwith the reason, androom.signalaborts. See what closes a room.
Log onError everywhere. It is the one place every problem in a running room shows up:
room.onError = (message) =>
logger.warn({ room: room.roomId, message }, 'room error');