Errors

Fail-closed admission and lifecycle errors

OpenRTC distinguishes budget denial, rate limiting, room capacity, identity/trust failures, unavailable coordination, and transport failures. Legacy errors remain recognized during migration.

Service errors

CodeMeaning and recovery
credit-exhaustedAccount credit is exhausted. Add authorized funding or wait for the stated reset; do not retry continuously.
app-budget-exhaustedThe app has reached its budget. Review its cap or allocation; another app may still have available funding.
app-rate-limitedThe app rate window is full. Respect retryAfterMs or resetAt when the service marks the operation retryable.
principal-rate-limitedThis principal has reached a rate limit. Respect the supplied retry timing without changing identities.
edge-rate-limitedAn edge abuse-protection limit rejected the request. Respect the supplied retry timing.
provider-safety-pausedHosted work is paused by a service safety control. Retry only if explicitly allowed; otherwise contact support with requestId.
usage-price-staleThe recorded usage price is no longer accepted for admission. Let the SDK refresh its authority; never reprice an existing settlement.
relay-budget-exhaustedRelay work cannot be admitted within its budget. Review authorized funding and relay limits.
room-capacity-exceededThe room has reached its explicit capacity. Choose an available room or wait for capacity; this is not a credit denial.

Service errors include code, scope, operation, retryable, requestId, and documentationUrl. Time-based recovery also includes retryAfterMs and/or resetAt (UTC Unix milliseconds). HTTP throttles use status 429 and Retry-After in seconds. Availability failures may use 503.

Firebase callable errors carry these fields in details; HTTP responses carry them at the top level. Use classifyError to preserve the service code and validated public metadata. RTCError.toJSON() and projectError retain recovery fields without copying arbitrary provider details. Older servers may omit the metadata.

Gateway edge throttles use edge-rate-limited with scope: ip for the caller-IP window or scope: avenue for the authenticated route window. TypeScript and Rust also recognize legacy edge-rate-exceeded and route-rate-exceeded responses, retaining explicit retryability and validated timing and request metadata. The gateway's 60-second retry hint is conservative backoff, not an exact reset time or a promise of admission. Older responses without timing do not acquire an invented reset timestamp.

Canonical service responses without an explicit boolean retryable: true do not authorize retry. Known legacy throttle aliases preserve their historical default; an explicit retryable: false always wins.

An app provider-safety-paused response includes retry timing only when committed short-window usage is the blocker and the stated reset would restore capacity with current reservations unchanged. Overlapping blocked windows use the latest required reset. Outstanding holds and monthly funded-capacity exhaustion have no automatic release promise; those denials remain untimed and non-retryable. Retry timing never guarantees admission after new intervening usage. Gateway upgrade denials and WebSocket error frames preserve only the documented public metadata, excluding internal accounting and credentials.

Browser observations after joining

Source-preview API: this callback is not included in published OpenRTC 2.5.4. Applications must wait for a compatible SDK release before adopting it.

The browser OpenRTC({ apiKey, onServiceError }) option observes typed coordination denials even after an avenue has joined. Each immutable ServiceErrorObservation includes its avenue kind/ID, canonical code, retryability and validated public metadata. It excludes server messages, causes, credentials and internal accounting. Known legacy edge codes are normalized through the same alias definitions.

Use the callback to render an actionable denial for the matching avenue. It is an error observation, not a connectivity status stream, recovery notification or instruction to reopen an avenue. Do not clear it merely because a peer list is empty, and do not create a timer or admission loop from the callback. Stopped/replaced browser sessions are fenced; synchronous and asynchronous observer failures cannot change SDK admission or recovery. Native forwarding is not yet implemented, so this callback is not native/CLI acceptance evidence.

Lifecycle and retry ownership

Capability admission validates origin, identity proof, replay, manifest, rate, and budget before creating live avenue state. Treat denial as authoritative. Error metadata is a projection, not a new retry owner: do not add timers, peer dial loops, or identity rotation to bypass a denial.

Retry only explicitly transient errors through the existing SDK owner, with bounded backoff and the same idempotency key. Honor server timing; a retry settles the same logical operation. Budget/price-authority renewal remains SDK-owned, even when retryable is true. Consumers should render a settled, actionable error rather than an indefinite loading indicator.

Emulator and endpoint overrides are internal test surfaces through openrtc/testing; public applications do not configure provider emulators.