Skip to content

Reference

Errors: Every Code and What To Do

Every error VoltKit RTC can return, from the REST API and the realtime connection: what each code means, whether to retry, and how the SDKs surface it.

Every error has a short, stable code. Branch on the code; show or log the message. Codes do not change between releases, messages may be reworded.

A blurry or frozen picture, echo, or a camera the browser will not open is not on this page: those come from the device or the network rather than from an error code. Troubleshooting shows how to tell which, and what to do, including the browser's own camera errors such as NotAllowedError.

How errors arrive

REST API. The HTTP status and a JSON body:

{ "error": "quota-exhausted", "message": "this account has used all of its minutes" }

Realtime connection. The reply to the request that failed:

{ "id": 7, "ok": false, "error": { "code": "forbidden", "message": "role viewer may not publish" } }

SDKs. The same code, on a typed error:

SDKError typeFields
Node.js (@voltkit-rtc/server)StreamErrorcode, status, retryable, plus isNotFound, isOutOfMinutes, isQuota, isCapacityExhausted, isBroadcastDisabled
Browser (@voltkit-rtc/client)RpcError, ChatRpcErrorcode, message
Browser, broadcast publishingWhipErrorstatus, isRefused
Flutter (voltkit_rtc)RpcErrorcode, message
Flutter, broadcast publishingWhipErrorstatus, isRefused

From the browser and Flutter SDKs 0.7.0 and the Node.js SDK 0.4.0, every SDK reads one registry of codes shared by all VoltKit SDKs. It says whether a code is terminal (a join refused with it is not retried) and whether retrying can help:

SDKRegistryAlso new
Browser 0.7.0ERROR_CODES, errorInfo(code), isTerminalError(code)every error the SDK raises is an RpcError with a code; RpcError.data carries what the server attached ({ slots } for slot-occupied)
Flutter 0.7.0errorCodes, errorInfo(), isTerminalError()RpcError.info, and RpcError.isTerminal reads the registry
Node.js 0.4.0ERROR_CODES, REST_ERROR_CODESStreamError.isBillingUnavailable

A newer service may send a code an older release does not know. It is passed through unchanged and is never treated as terminal.

// Node.js
try {
  await rtc.ensureRoom({ roomId, hostIdentity });
} catch (err) {
  if (err.isOutOfMinutes) return res.status(402).send("Top up to go live");
  if (err.retryable) return retryLater();
  throw err;
}
// Browser
import { RpcError } from "@voltkit-rtc/client";

try {
  await client.sendMessage(text);
} catch (err) {
  if (err instanceof RpcError && err.code === "chat-muted") showMutedNotice();
  else if (err instanceof RpcError && err.code === "rate-limited") showSlowDown();
}
// Flutter
try {
  await client.sendMessage(text);
} on RpcError catch (e) {
  if (e.code == 'chat-muted') showMutedNotice();
}

REST errors

StatuserrorWhat happenedWhat to do
400bad-requestA field is missing, the wrong type, or not allowed. The message says which.Fix the request. Do not retry as it is.
400bad-rolerole is not host, cohost, moderator or viewerSend one of the four
401unauthorizedThe API secret is missing, wrong or has been revokedCheck the secret on the Credentials tab; issue a new one if it leaked
402quota-exhaustedThe project has used every minute it may use (free, its own and the account balance), and every wallet share it may spend is emptyRecharge the VoltKit Wallet, or move credits into the project's shares. Turn on Use account balance for the project if the balance still has minutes, or wait for free minutes to renew. Anything live is warned and then ended. See which minutes are used first
503billing-unavailableThe project is billed from the VoltKit Wallet and no price list is active on the service yetContact support. A retry does not help, so the SDKs do not retry it
402daily-limit-reachedThe project reached its usage limit for todayWait for midnight UTC, or ask for a higher limit
402monthly-limit-reachedThe project reached its usage limit for this monthWait for the next month, or ask for a higher limit
403tenant-suspendedThe project has been suspended, by support or because the account was disabledContact support
404no-roomThe room does not exist, has ended, or belongs to another projectCreate it, or treat the session as over
404no-streamNo such broadcast streamCreate it
404no-messageNo such chat message in that channelNothing: it is already gone
404broadcast-disabledBroadcast is not switched on for this serviceNot something a retry fixes
404chat-disabledChat is not switched on for this serviceNot something a retry fixes
409room-existsYou already have a live room with that idUse PUT /v1/rooms/{id} (ensureRoom) to get it back instead
409stream-existsYou already have a stream with that idUse the existing stream, or delete it first
429rate-limitedToo many API calls this minuteBack off and retry. The SDK does this for you.
429room-quotaThe project is at its limit of rooms running at onceEnd rooms that are no longer needed
429viewer-quotaThe room or channel is at its member limitWait for space, or ask support to raise the limit
429stream-quotaThe project is at its limit of streamsDelete streams that are no longer needed
503capacity-exhausted, no-nodesThere is no capacity for a new room right nowRetry shortly. Worth alerting on if it persists.
503unavailableSomething the request depends on could not be checkedRetry shortly
401bad-signatureA signed request or webhook whose signature does not verifyCheck the signing secret. The Node.js SDK's parseWebhook raises it for a payload not signed with your webhook secret
403operator-onlyThat route describes the service's own infrastructureNot available to projects
404not-foundNo such resourceCheck the path and the id
500internalSomething failed on our sideRetry once; tell us if it repeats

401, 402, 403 and 429 rate-limited can come back from any call.

Realtime errors

codeWhat happenedWhat to do
no-token, bad-tokenThe token is missing, expired, or for a different kind of connection (a chat token on a room connection, or the reverse)Ask your backend for a new token. Do not retry with the same one.
token-mismatchrefreshToken (updateToken in the SDKs, 0.7.0) sent a valid token for another project, room or identity than this sessionMint one for this room and identity. The session keeps the token it had
wrong-tenantReserved: a token for another project's room (current servers answer bad-token or no-room)Ask your backend for a new token
kickedThis identity was removed from the room; tokens minted before cannot rejoin itLeave the screen. The SDKs stop reconnecting by themselves
billing-unavailableThe project is billed from the VoltKit Wallet and no price list is active yetContact support. The SDKs stop reconnecting by themselves
not-joinedA request was sent before join finishedWait for connect() to resolve
too-many-requestsMore than 32 requests are waiting for a reply on one connectionWait for replies before sending more
not-in-roomYour participant record is gone: the room is ending or you were removedTreat the session as over
no-nodeThe media server assigned to you went awayRetry; the SDKs reconnect by themselves
source-already-publishedThat source (camera, microphone, screen, music) is already publishedStop it before publishing it again
no-transport, no-producer, no-consumerThe media server no longer knows that transport, track or subscription: it closedRefresh your state; the SDKs handle it
consumer-cap, producer-cap, room-capThe media server refused rather than degrade everyoneRetry shortly
drainingThe media server is being taken out of serviceRetry; you are placed elsewhere
chat-unavailableChat is not switched on for this serviceNot something a retry fixes
already-joinedjoin was sent twice on one connectionNothing: you are in
no-room, room-endedThe room does not exist or is overLeave the screen. The SDK stops reconnecting by itself.
participant-quotaA free testing trial room has 25 participants, or an explicit operator limit has been reachedWait for a participant to leave. The first minute top-up for the trial project, or for the account balance it uses, removes the trial restriction; paid bundles do not set room caps. See trial conditions.
quota-exhausted, daily-limit-reached, monthly-limit-reached, tenant-suspendedThe project cannot use the service right now. join refuses even a token minted earlier, so an ended session cannot be rejoinedTop up or wait for the limit to reset. The SDKs stop reconnecting by themselves.
forbiddenYour role does not allow that: a viewer publishing, a viewer muting someoneHide the control for that role
force-mutedA moderator muted you, and you tried to unmuteWait until a moderator releases it
no-participantThe person you named is not in the roomRefresh the roster
no-capacityNo media server can take you right nowRetry shortly
node-errorThe media server refused or failed the requestRetry once; reconnect if it repeats
cannot-consumeThis viewer's browser cannot play the codec a host sends, usually H.264 from a phone on a browser built without itNot something a retry fixes. Suggest Chrome, Edge, Safari or Firefox. See a viewer's browser cannot play H.264
bad-transportA media request named a transport that is not yoursA client bug if you are writing your own; the SDKs never do this
chat-mutedA moderator muted you in this chatTell the user. Do not retry.
rate-limitedToo many chat messages too quicklySlow down; the limit refills within seconds
no-messageThat message does not exist, or is not yours to seeRemove it from the list
slot-occupied, slot-taken, slot-blockedA seat is held by someone else, or lockedOffer another seat
bad-params, bad-jsonThe request was malformed. The message says how.Fix the request
unknown-methodNo such method on this connection. Media methods do not exist on a chat-only connection.Use a room connection for media
closed, disconnectedThe connection closed before the reply arrived. The JavaScript SDK rejects the call with a plain Error instead of a codeThe SDK reconnects; retry once it has
internalSomething failed on our sideRetry once

Video profile and screen-share errors (SDK 0.5.0)

CodeMeaningWhat to do
stage-fullAll three demo co-host places are occupiedThe host can demote a co-host before accepting another. Becoming a co-host does not free an audience place.
quality-unavailableAn older producer does not provide usable layer metadataChoose Auto or update the publisher. Explicit presets map real layers; they cannot manufacture a resolution.
frame-rate-fallbackThe camera, device or upload cannot provide the requested 60 fps. Before 0.7.0 it also covered a project that does not allow 60 fps, which is now frame-rate-not-allowedContinue at 30 fps and update the control. Browser emits an error warning; Flutter uses onPublishWarning (and onError from 0.7.0). It is not a reconnect signal.
screen-share-unsupportedBrowser/platform does not support capture, or the prepared iOS preview is not opted into / requires iOS 14+Offer supported capture; iOS preview requires separate setup and remains unverified.
screen-share-activeA share is already starting or liveStop it before starting another.
screen-share-no-videoBrowser capture supplied no live video trackChoose a screen/window/tab again.
screen-share-service-requiredAndroid foreground-service callbacks are missingFollow the Android setup.
screen-share-source-requiredFlutter desktop capture lacks a selected sourceChoose a screen/window and pass its id.
screen-share-deniedAndroid capture permission was declinedLet the user retry; do not restart capture in the background.
screen-share-size-unavailableFlutter cannot read a captured screen frameCheck permission, selected source and its actual dimensions.
screen-share-failedFlutter capture/setup/restore failedKeep the camera running, show feedback and retry screen capture if requested.
screen-share-setup-requiredPrepared 0.6.0 iOS preview lacks its native plugin, embedded extension or matching signed App GroupFollow the iOS extension guide and rebuild the app.
screen-share-start-timeoutiOS broadcast did not start before the configured deadlineLet the person retry from the app and select Start Broadcast in the system sheet.
screen-share-endediOS extension stopped while capture was startingShow that the share ended; camera/mic can continue.

Browser chooser cancellation can also reject with native NotAllowedError or AbortError; no screen was shared. Camera, microphone and music do not need to be stopped. Host/co-host permissions and server moderation apply to screen capture too; a viewer receives forbidden.

Media settings and wallet billing errors

A project's media settings say what it may publish. The browser and Flutter SDKs 0.7.0 read them when they join and never send what would be refused: they either refuse the call or send the most the project allows and warn, with the same code the server would use. The media server enforces the settings whatever a client sends; the codes it raises itself (marked server) apply to sessions billed from the VoltKit Wallet: every account, unless VoltKit keeps it on minute billing by agreement. Do not retry the same request: publish within the settings instead.

CodeWhat happenedSDK behaviour
media-disabledThe project switched that kind of media (audio, or video: camera and screen alike) off, or no enabled mode fitsRefused before any device opens
frame-rate-not-allowedThe project does not allow 60 fpsSent at 30 fps, with a warning; setVideoFrameRate(60) changes nothing and warns again
video-quality-not-allowed60 fps is allowed, but not at the size asked forThe largest size allowed at 60 fps goes out, with a warning
audio-quality-not-allowedHD audio in a project limited to standard audiosetAudioQuality('hd') is refused; HD asked for in publish() goes out as standard, with a warning. In-app music (128 kbps stereo) is not affected: it is free and allowed in every project
video-not-allowed, audio-not-allowedserver: video or audio is turned off for the project, or no mode it allows fits the streamNot raised by a current SDK, which refuses first with media-disabled
video-subscription-limitThis participant already receives the most videos allowed at once (25 by default)The next video waits until one ends, with one warning each

A 30 fps quality above the project's largest is not an error: the SDKs and the server cap it silently.

Codes the SDKs raise themselves

New in the browser and Flutter SDKs 0.7.0 and the Node.js SDK 0.4.0. Warnings arrive on error (browser) or onError and onPublishWarning (Flutter) and stop nothing; refusals reject the call.

CodeSDKWhat happenedWhat to do
not-connectedbrowserThe call needs a joined connection and there is none right nowRetry once connectionState is connected
timeoutbrowser, FlutterThe service did not answer within 15 secondsRetry
connect-failedbrowser, FlutterThe connection could not be openedCheck the network; retry
disconnectedFlutterThe connection is not open, or dropped while the call waitedRetry after the reconnect
replacedbrowser, FlutterThe same identity connected from somewhere else; this session is closedDo not reconnect
reconnect-gave-upbrowser, FlutterEvery reconnect attempt failed; the session is closedStart a new session
devices-gonebrowser, FlutterA reconnect could not publish the broadcast again: its tracks had endedCapture again and publish
unpublishedbrowser, Flutterunpublish() ran while that publish was under wayNothing: unpublishing wins
codec-fallbackbrowser, FlutterA forced H.264 could not be used, so VP8 is sentNothing, or stop forcing H.264
produce-failedFlutterThe media library did not deliver a track to the serverRetry; the platform log says why
consume-failedFlutterOne remote track could not be received; the rest of the room worksRetry the subscription
unknown-producerFluttersetReceiveQuality named a video this client does not receiveUse the producerId from onTrack
screen-audio-unavailableFlutterThe platform granted no display audio; the screen is shared without soundNothing
screen-stop-failedFlutterA capture could not be stopped cleanly; the share has ended anywayNothing
degradation-preference-failedFlutterThe encoder ignored the degradation preferenceNothing; a busy device lowers resolution first
networkNode.jsThe request never reached the service (DNS, refused, timeout)Retried by the SDK where safe
rates-unconfiguredNode.jsgetRates() was called without ratesBaseUrlSet ratesBaseUrl to the VoltKit website API
redirectedNode.jsThe service answered a request carrying your API secret with a redirect, which the SDK never followsSet baseUrl to the exact scheme and host; see the server SDK

When an account runs out mid-session

A live session is not left running when its project runs out. Clients first receive usageLimit with { reason, graceSeconds, endsAt }; about a minute later the session is ended: rooms with roomClosed (its reason is the same code), chat-only connections with a final usageLimit. Then the server closes the connection with WebSocket close code 4003.

Close codeMeaningReconnect?
4001The same identity joined from somewhere elseNo
4003Ended by the server, after it said whyNo — it would be refused
any otherThe connection droppedYes; the SDKs do it for you

Rooms are warned while the minutes and credits a project may spend cover less than about a minute at the current rate: walletLow with remainingSeconds (and usageLimit, for older apps). A recharge in time sends walletLow with cleared: true, and nothing ends. If the credits reach zero, the rooms close at once with roomClosed (reason quota-exhausted) and close code 4003. See Running low and running out.

Why a session ended

When a client's session is over for good, the SDKs say why instead of leaving you to guess:

ReasonMeaning
leftYou called close()
replacedThe same identity joined the same room from somewhere else. Do not reconnect, or the two sessions will keep evicting each other.
kickedA moderator removed you
room-closedThe host or your backend ended the room
gave-upThe connection dropped and could not be restored after repeated attempts

Broadcast publishing errors

Publishing a broadcast is one HTTP exchange with the media server. A failure is a WhipError:

statusMeaningWhat to do
401, 403 (isRefused)The publish URL was not accepted: it expired, is for another stream, or the project is out of hoursGet a new URL from your backend. The same one will be refused again.
0The media server could not be reached, or did not answer in timeCheck the connection and try again

Still stuck

Send the error code, the time, and your App ID (never your API secret) to contact@voltkit.in, or open a ticket from the Support page of your dashboard. For a quality or connection problem, Troubleshooting lists what to include so we can find your session.

Last updated . Something unclear or wrong? Tell us.