Close codes
When authentication fails, the gateway closes the WebSocket immediately, before any handshake, with one of three custom close codes. The SDK surfaces these as fatal errors and stops reconnecting — exponential backoff on a bad credential serves no one.
The codes
Section titled “The codes”| Code | Meaning | When it fires |
|---|---|---|
4001 | Missing apiKey | The query string had no apiKey parameter. Should not happen via createClient — the SDK appends it for you. |
4002 | Invalid apiKey | The provided key isn’t on the gateway’s accept list. Either it was never issued, was rotated out, or was mistyped. |
4003 | Quota / rate-limit exceeded | Reserved — not enforced today. Will be used when per-key quotas come online. |
These are in the application range (4000-4999) reserved by the WebSocket protocol for application-defined codes.
How the SDK reacts
Section titled “How the SDK reacts”On any of 4001, 4002, 4003:
- The
disconnectedlifecycle event fires withcode: 4001 | 4002 | 4003,willReconnect: false. - An
errorevent fires withfatal: trueand a human-readable message. - The reconnect loop is stopped. The client will not retry.
- If
connect()is in flight, the returned promise rejects with the same message.
client.on('error', ({ message, fatal }) => { if (fatal) { // alert the user, surface in your UI, log to telemetry console.error('auth failed:', message) }})
try { await client.connect()} catch (err) { // err.message reflects the close code: "missing apiKey", "invalid apiKey", etc.}Distinguishing 4001 and 4002
Section titled “Distinguishing 4001 and 4002”Practically, 4002 is what you’ll hit when something’s wrong with your key (typo, rotation, wrong env). 4001 should never reach a properly-configured client because createClient({ apiKey }) requires the key to construct.
If you do see 4001, double-check that your URL doesn’t already carry conflicting apiKey query parameters that get stripped or duplicated.
What’s not in this set
Section titled “What’s not in this set”Other WebSocket close codes you might see (1000 normal, 1001 going away, 1006 abnormal, 1011 server error) are not auth failures. The SDK treats them as transient and reconnects with backoff.
See Reconnect tuning for how to bound or customise that loop.