Raven Docs

Error Codes

One canonical namespace, plus the two wire protocols that deliberately keep their own.

Raven has three error vocabularies, and they are separate on purpose:

VocabularyWhere you see itWhy it is its own thing
RAVEN_* codesThe code field of any HTTP error bodyOne namespace for the whole REST API, so a single switch handles every endpoint
RTC categoriesDashboard, raven errors, telemetryA classification of a failure that already happened, derived server-side from what the SDK reported — not a response code
Chat frame codesThe chat WebSocket error frameA published wire protocol with its own lifetime; see WebSocket Protocol

A chat failure and a media failure have almost nothing in common, and merging them would produce a list that describes neither well.


The HTTP error envelope

Every error the REST API returns has the same shape:

{
  "code": "RAVEN_ROOM_NOT_FOUND",
  "legacyCode": "NOT_FOUND",
  "message": "Room not found",
  "requestId": "req_9f2c41ab77e0c3d5b1a4e8f2",
  "path": "/v1/rooms/room_missing"
}
FieldNotes
codeThe canonical code. Switch on this.
legacyCodeDeprecated — see below.
messageHuman-readable, safe to log, never contains credentials or internals.
requestIdQuote this in a bug report. Also returned as the x-request-id header, always with the same value.
pathThe route that produced the error.

Some errors add fields — a 429 carries retryAfterSeconds, for example. Unknown fields should be ignored rather than treated as an error.

Request IDs

Every response carries x-request-id. Error bodies repeat it as requestId so a developer copying a JSON blob into an issue does not lose it.

Send your own and Raven will adopt it, letting one call be traced across your logs and ours:

x-request-id: 7c1f9e2a-your-own-correlation-id

An inbound value is accepted only when it is 1–64 characters of A-Za-z0-9_-. Anything else — a newline, a control character, a megabyte of text — is discarded and a fresh ID generated. That value ends up in log lines and error bodies, so a half-sanitised identifier is worth less than an honest new one. Generated IDs look like req_ followed by 24 hex characters.

Canonical codes

CodeHTTPMeaning
RAVEN_AUTH_ERROR401Credentials missing, malformed, or rejected.
RAVEN_TOKEN_EXPIRED401Distinct from the above: refresh, do not re-authenticate.
RAVEN_PERMISSION_DENIED403Authenticated, but not allowed to do this.
RAVEN_NOT_FOUND404Generic; used when no resource-specific code fits.
RAVEN_PROJECT_NOT_FOUND404
RAVEN_ROOM_NOT_FOUND404An RTC room.
RAVEN_CONVERSATION_NOT_FOUND404A chat conversation.
RAVEN_MESSAGE_NOT_FOUND404
RAVEN_ATTACHMENT_NOT_FOUND404
RAVEN_STREAM_NOT_FOUND404A live stream.
RAVEN_CONFLICT409Generic conflict — a name already taken, a state already reached.
RAVEN_MESSAGE_ALREADY_EXISTS409An idempotency key was replayed.
RAVEN_CONVERSATION_ARCHIVED409Unarchive it first.
RAVEN_STREAM_INVALID_STATE409A lifecycle operation that isn't valid from the stream's current status — e.g. starting an already-LIVE stream.
RAVEN_VALIDATION_FAILED400The request body or query is malformed.
RAVEN_INVALID_CURSOR400Pagination cursor unreadable — do not fall back to page one.
RAVEN_PAYLOAD_TOO_LARGE413Generic size limit.
RAVEN_MESSAGE_TOO_LARGE413The message body limit specifically.
RAVEN_ATTACHMENT_TOO_LARGE413The attachment limit, configured separately from the above.
RAVEN_RATE_LIMITED429Carries retryAfterSeconds.
RAVEN_CONNECTION_FAILEDA realtime connection could not be established.
RAVEN_WEBHOOK_FAILEDA webhook delivery failed.
RAVEN_NOT_CONFIGURED501The deployment has not enabled this feature. An operator fix, not a caller one.
RAVEN_INTERNAL_ERROR500The only code an unexpected exception ever surfaces as.

Codes are grouped so that anything a caller would handle the same way shares one code, and anything needing a different fix gets its own. RAVEN_MESSAGE_TOO_LARGE and RAVEN_ATTACHMENT_TOO_LARGE are separate because the two limits are configured independently — "make it smaller" is not actionable until you know which limit you crossed.

SDK-side codes

The server SDKs use the same namespace for failures that never reach the API, so one switch covers everything:

RAVEN_TIMEOUT, RAVEN_NETWORK_ERROR, RAVEN_INVALID_CONFIG, RAVEN_UNKNOWN_ERROR.

When a proxy returns an HTML error page instead of JSON, the SDK derives the code from the status — and derives it to the same name the API would have sent, so a 401 is RAVEN_AUTH_ERROR either way.

legacyCode and the migration

Before this namespace existed, code held bare values: NOT_FOUND, UNAUTHORIZED, VALIDATION_FAILED, and the chat codes such as INVALID_CURSOR. Anything switching on those keeps working: every error body now carries both, with legacyCode holding exactly what that error used to emit.

// Old — still works, for now
if (error.code === 'NOT_FOUND') { ... }        // now error.legacyCode
 
// New
if (error.code === 'RAVEN_ROOM_NOT_FOUND') { ... }

legacyCode is deprecated and will be removed. Nothing in this repository reads it; it exists purely for callers we cannot see. Migrate by switching on code and deleting any reference to legacyCode.

Note that legacyCode is lossy in one direction: several canonical codes map back to the same legacy value (RAVEN_ROOM_NOT_FOUND and RAVEN_MESSAGE_NOT_FOUND were both NOT_FOUND). That is the point — the new codes carry information the old ones did not.


RTC errors

Every RTC error a developer sees — in the dashboard, in raven errors, or in an @corvidhq/rtc error event — is a Raven concept, never a raw SFU or TURN error code. One place server-side maps the underlying error into this taxonomy.

Categories

CategoryMeaning
AUTHENTICATION_ERRORThe caller's own identity/credential was rejected.
AUTHORIZATION_ERRORAuthenticated, but not allowed to do this (e.g. token lacks a permission).
TOKEN_ERRORThe RTC token itself was invalid, malformed, or expired.
SIGNALING_ERRORThe signaling handshake to the RTC endpoint didn't complete.
ICE_ERRORICE connectivity checks failed — usually a firewall/NAT restriction.
TURN_ERRORA TURN relay connection specifically could not be established.
SFU_ERRORThe media server couldn't complete the connection, for no more specific reason.
NETWORK_ERRORA generic network-level failure or timeout.
CLIENT_ERRORA local/application-side issue — wrong room, device permission, media error.
UNKNOWN_ERRORNothing more specific could be determined.

SDK error code → category mapping

@corvidhq/rtc RTCErrorCodeCategory
INVALID_TOKEN, TOKEN_EXPIREDTOKEN_ERROR
PERMISSION_DENIEDAUTHORIZATION_ERROR
ROOM_NOT_FOUNDCLIENT_ERROR
CAMERA_PERMISSION_DENIED, MICROPHONE_PERMISSION_DENIED, DEVICE_NOT_FOUND, MEDIA_ERRORCLIENT_ERROR
TIMEOUT, NETWORK_ERRORNETWORK_ERROR
SIGNALING_ERRORSIGNALING_ERROR
CONNECTION_FAILEDTURN_ERROR / ICE_ERROR / SIGNALING_ERROR / SFU_ERROR — see below
anything elseUNKNOWN_ERROR

CONNECTION_FAILED is context-dependent, checked in this order:

  1. A hint: 'turn_unreachable' in the reported data → TURN_ERROR.
  2. iceConnectionState of failed/disconnectedICE_ERROR.
  3. signalingState never reached stable/connectedSIGNALING_ERROR.
  4. Otherwise → SFU_ERROR (no more specific signal available).

Smart explanations — hedged, never certain

Every classified error carries a likelyCause and suggestedAction — deliberately hedged language ("likely a firewall/NAT restriction"), never a claim of certainty a Raven server can't actually back up. Examples:

  • TOKEN_ERROR: "The RTC token had already expired before (or during) the connection attempt.""Mint a fresh RTC token — tokens are always short-lived by design."
  • ICE_ERROR: "ICE connectivity checks failed between the client and the media server — likely a firewall/NAT restriction.""Ensure TURN is reachable from this network; corporate proxies/firewalls are the most common cause."
  • TURN_ERROR: "Unable to establish a TURN relay connection — possibly a restrictive firewall/NAT blocking UDP.""Check whether UDP traffic is blocked; try a TCP/TLS TURN transport instead."

Where to see this

  • raven errors list / raven errors inspect <errorId> — see CLI
  • Dashboard → a project's Errors tab and error detail page
  • GET /v1/projects/:projectId/errors / /errors/:errorId (JWT-guarded)

Chat frame codes

These are the codes on the WebSocket error frame. Over HTTP the same failures arrive as RAVEN_* codes (with the chat code preserved in legacyCode) — see the envelope section above. The frame keeps its own vocabulary because it is a separately versioned wire protocol that @corvidhq/chat already maps.

Every failure from the chat API or @corvidhq/chat carries one of these codes. They are stable, and they map one-to-one onto SDK error classes so a caller can branch on the class rather than string-matching a message.

Raw infrastructure errors never reach a client: a Postgres constraint violation, a Redis timeout, or an unhandled exception is logged server-side in full and surfaces as INTERNAL_ERROR.

CodeHTTPSDK classMeaning
INVALID_TOKEN401RavenChatAuthenticationErrorMissing, malformed, or wrongly-signed chat token.
TOKEN_EXPIRED401RavenChatAuthenticationErrorThe token expired — mint a new one.
TOKEN_REVOKED401RavenChatAuthenticationErrorRevoked before its natural expiry.
UNAUTHORIZED401RavenChatAuthenticationErrorNo usable credential presented.
PERMISSION_DENIED403RavenChatPermissionErrorAuthenticated, but the scope or role doesn't allow this.
NOT_A_MEMBER403RavenChatPermissionErrorNot a member of that conversation.
ORIGIN_NOT_ALLOWED403RavenChatPermissionErrorThe upgrade's Origin isn't in CORS_ORIGIN.
ROOM_NOT_FOUND404RavenRoomErrorNo such conversation in this project.
NOT_IN_ROOM400RavenRoomErrorThis connection isn't subscribed to that room.
TOO_MANY_SUBSCRIPTIONS400RavenRoomErrorPer-connection room subscription limit reached.
CONVERSATION_ARCHIVED409RavenRoomErrorWrites are closed; reads still work.
MESSAGE_NOT_FOUND404RavenMessageErrorNo such message in this project.
MESSAGE_DELETED409RavenMessageErrorThe message is soft-deleted.
MESSAGE_TOO_LARGE413RavenMessageErrorText, metadata, or frame exceeded its limit.
INVALID_MESSAGE400RavenMessageErrorMalformed or missing a required field.
INVALID_MESSAGE_TYPE400RavenMessageErrorUnsupported frame or message type.
INVALID_CURSOR400RavenMessageErrorPagination cursor is malformed.
RATE_LIMITED429RavenRateLimitErrorA limit was exceeded; carries retryAfterSeconds.
ATTACHMENT_NOT_FOUND404RavenAttachmentErrorNo such attachment, or not yet uploaded.
ATTACHMENTS_NOT_CONFIGURED501RavenAttachmentErrorNo object storage configured on this deployment.
ATTACHMENT_TOO_LARGE413RavenAttachmentErrorOver STORAGE_MAX_ATTACHMENT_BYTES.
CONNECTION_FAILEDRavenChatConnectionErrorCould not connect, or reconnects were exhausted.
CONNECTION_CLOSEDRavenChatConnectionErrorThe socket closed before the server replied.
NETWORK_ERRORRavenChatConnectionErrorThe request never reached Raven.
TIMEOUTRavenChatConnectionErrorNo server response within requestTimeoutMs.
INTERNAL_ERROR500RavenChatErrorSomething failed on Raven's side; logged server-side.

An unrecognised code (from a newer server) becomes a base RavenChatError with that code preserved, rather than an exception — an older client keeps working across a server upgrade.

WebSocket close codes

CodeMeaningReconnect?
1000Normal closureNo
4401Authentication failedNo — retrying can't help
4403Origin not allowedNo
4429Connection rate limitYes, after backing off
4440Token expiredYes, with a fresh token
4500Server shutting downYes, after backing off

Where to see this

  • Dashboard → a project's Chat section
  • @corvidhq/chat's error event and rejected promises
  • WebSocket Protocol for the frame-level contract