Search documentation

Search pages and headings

API Reference

Errors

The two places an error can appear, every error code with its HTTP status, and which ones are worth retrying.

Two shapes, one vocabulary

An error reaches you in one of two forms depending on when it happened.

Before the stream opens

An ordinary HTTP error with a real status code and a JSON body:

{
  "error": {
    "code": "NO_LIVE_CHAT",
    "message": "no live chat found - video may not be live, have no chat, or be unavailable"
  }
}

After the stream opens

The status line has already been sent, so the failure arrives as a terminal SSE frame instead:

event: error
data: {"code":"UPSTREAM_ERROR","message":"..."}

Same code vocabulary, no status code. The frame is terminal: the body closes after it.

code is a stable enum and is what you should branch on. message is human-readable, may be passed through from YouTube, and may change.

Every code

Status Code Meaning
400 INVALID_REQUEST A query parameter or request body failed schema validation
400 INVALID_VIDEO_ID videoId was neither a recognized YouTube URL nor a bare 11-character id
404 NO_LIVE_CHAT No liveChatRenderer or liveChatReplayRenderer with a continuation token could be found for the video
410 TOKEN_EXPIRED The continuation token was rejected. Rare as a client-visible error — the server repairs this itself
422 UNKNOWN_CLIENT An innertube client label was not recognized
429 RATE_LIMITED YouTube is throttling this server's IP. Carries a Retry-After header
502 SW_JS_FAILED sw.js could not be fetched, or the API key / client version could not be parsed out of it
502 NO_WORKING_CLIENT Neither WEB nor TVHTML5 produced a usable response on either endpoint
502 UPSTREAM_ERROR YouTube returned an error for the request
500 INTERNAL An unexpected server-side failure, including a storage failure

Notes on individual codes

INVALID_VIDEO_ID

The id must be extractable. These forms all work:

zvwJ29RFVww
https://www.youtube.com/watch?v=zvwJ29RFVww
https://youtu.be/zvwJ29RFVww
https://www.youtube.com/shorts/zvwJ29RFVww
https://www.youtube.com/embed/zvwJ29RFVww
https://www.youtube.com/live/zvwJ29RFVww

A channel URL or a playlist URL does not contain a video id and will fail.

NO_LIVE_CHAT

This is a 404 because it describes the video, not a fault. It covers several real situations that are indistinguishable from the outside:

  • the video is not live and has no replay chat,
  • chat was disabled for the broadcast,
  • replay chat has not finished processing, or has been turned off,
  • the video is private, deleted, or region-blocked.

Retrying will not help unless the underlying situation changes.

RATE_LIMITED

YouTube throttles by IP, and the server shares one. A 429 carries a Retry-After header in seconds — honour it. If it arrives as an error frame instead there is no header, so wait at least 30 seconds, which is what the server itself falls back to.

Reconnecting immediately is actively harmful: your reconnect drives a fresh bootstrap against the endpoint that is already refusing traffic.

TOKEN_EXPIRED

Mid-stream token expiry is the server's problem now, not yours. The poller re-bootstraps the room in place and the stream continues without a break. You will only see this code if that repair itself fails.

SW_JS_FAILED

The server refuses to hardcode the innertube API key and fetches it live per bootstrap. If YouTube changes the shape of sw.js, this is the code you get. It is a server-side problem, not something a caller can fix.

UPSTREAM_ERROR

Worth knowing: YouTube signals innertube failures with HTTP 200 and an error object in the body. The server treats that as a failure rather than an empty success, which is why a genuine upstream problem surfaces here as a 502 instead of silently looking like a quiet chat.

INTERNAL

Also covers storage failures. Which storage backend the server is running against is not exposed on the wire, deliberately — the detail is in the server's logs.

Which errors are retryable

Code Retry?
RATE_LIMITED Yes — after Retry-After, never sooner
UPSTREAM_ERROR Yes, with backoff — often transient
INTERNAL Yes, with backoff
SW_JS_FAILED Yes, with backoff, but repeated failures need a fix
TOKEN_EXPIRED Yes — reconnect; the server re-bootstraps
NO_WORKING_CLIENT Rarely — usually means the video is not readable
NO_LIVE_CHAT No
INVALID_VIDEO_ID No
INVALID_REQUEST No
UNKNOWN_CLIENT No

The client library implements exactly this policy, with two separate budgets: ordinary failures back off exponentially from 500 ms, capped at 15 000 ms, giving up after 3; rate limits wait out Retry-After and get their own allowance of 8, reset by any successful reconnect. A server that simply hangs up is treated as a reconnect and costs neither budget. See Events & Options.

Errors in the client library

The client raises every failure as an ApiHttpError:

import { ApiHttpError } from '@gettersethya/yt-livechat-client'

client.on('error', (error) => {
  // error.code         -> the enum above
  // error.message      -> human-readable detail
  // error.status       -> HTTP status, or 0 if there was no response
  // error.retryAfterMs -> set on a rate limit; how long to wait
  if (error.code === 'NO_LIVE_CHAT') stopTrying()
})

status is 0 when the failure did not come from an HTTP response — a network error, an error frame, or an invalid video URL rejected in the constructor.

An error event is not fatal. The client emits it and then decides whether to continue; only end means the stream is over.