Files
documentation/guides/error-handling.md
2026-11-14 18:21:12 -08:00

5.1 KiB
Raw Blame History

Error Handling & Resilience Guide

Strategies, error classification, and recovery patterns for handling network stalls, upstream rate limits, and streaming failures.

Overview

Streaming from distributed upstream web providers is inherently dynamic. Upstream CDN nodes rotate domains, regional ISPs implement traffic shaping, and token signatures expire.

Building a resilient client requires planning for failure at every step. This guide explains how Vyla handles errors internally and how client players should respond.


Error Classification Matrix

Error Type Common Cause Stream API / SDK Behavior Recommended Player Action
HTTP 400 Bad Request Missing or malformed TMDB ID, invalid season/episode Returns immediate JSON error payload without querying providers. Validate input parameters client-side before sending requests.
HTTP 429 Too Many Requests Upstream host rate-limiting client IP SDK provider applies backoff delay and retries up to retries limit. If proxy returns 429, respect the retry_after header and back off.
HTTP 502 Bad Gateway Upstream provider host is unreachable or timed out Provider is discarded; other scrapers continue executing. Fall back to the next available stream candidate in your queue.
SSE Disconnect Network hiccup or client tab backgrounded Connection drops; EventSource.onerror fires. If video is already playing, do not interrupt. Otherwise, reconnect after 2 seconds.
HLS Fatal Media Error Segment buffer stall, corrupt chunk, or missing decryption key Player triggers Hls.Events.ERROR with fatal: true. Call hls.destroy() and load the next candidate from your fallback queue.

Client Fallback Queue Strategy

Always treat streaming candidate resolution as a prioritized queue rather than a single static link:

Queue Priority Assignment

Candidate Stream Player State Fallback Precedence
Candidate 1 (vidlink) Active Playback Mounted into HLS.js or ExoPlayer on arrival.
Candidate 2 (vixsrc) Standby Backup Enqueued as Priority #1 fallback stream.
Candidate 3 (cinecat) Standby Backup Enqueued as Priority #2 fallback stream.

Recovery Sequence on Stream Stall

  1. Error Interception: Player detects a fatal network or decode event (data.fatal === true).
  2. Timestamp Preservation: Record current playback offset (e.g. currentTime = 142.5s).
  3. Buffer Teardown: Call hls.destroy() or player.release() to release media pipelines and decoders.
  4. Queue Advancement: Dequeue the next available verified candidate from the standby queue.
  5. Seamless Seek: Mount the fallback source and seek immediately to the preserved timestamp upon loadedmetadata.

Implementing Fallback Recovery in JavaScript

function attachHlsPlayer(videoElement, candidate, fallbackQueue) {
  const hls = new Hls({
    maxBufferLength: 30,
    maxMaxBufferLength: 60,
    enableWorker: true
  });

  hls.loadSource(candidate.url);
  hls.attachMedia(videoElement);

  hls.on(Hls.Events.ERROR, (event, data) => {
    if (!data.fatal) return;

    console.warn(`Encountered fatal error on ${candidate.source}:`, data.type);

    switch (data.type) {
      case Hls.ErrorTypes.NETWORK_ERROR:
        console.log("Attempting network recovery...");
        hls.startLoad();
        break;
      case Hls.ErrorTypes.MEDIA_ERROR:
        console.log("Attempting media recovery...");
        hls.recoverMediaError();
        break;
      default:
        hls.destroy();
        if (fallbackQueue.length > 0) {
          const nextCandidate = fallbackQueue.shift();
          const lastTime = videoElement.currentTime;
          console.warn(`Switching to fallback candidate: ${nextCandidate.label}`);

          attachHlsPlayer(videoElement, nextCandidate, fallbackQueue);
          videoElement.addEventListener('loadedmetadata', () => {
            videoElement.currentTime = lastTime;
            videoElement.play();
          }, { once: true });
        } else {
          showErrorOverlay("All available streaming providers have failed.");
        }
        break;
    }
  });

  return hls;
}

Client-Side Timeout Budgets

Never leave users hanging on an indefinite spinner. Implement a two-tiered timeout budget:

  1. Initial Candidate Timeout (8–10 seconds): If no type: "source" event has arrived within 8 seconds, update the UI to indicate extended searching.
  2. Hard Timeout (20 seconds): If the stream has not completed or emitted a viable candidate within 20 seconds:
    • Call eventSource.close().
    • Render a helpful empty-state UI with a "Retry" button or an option to change the server URL.

Decoupled Subtitle Fallback

Subtitles originate from independent subtitle repositories (e.g. OpenSubtitles, SubDL). If a subtitle track fails to load:

  • The video playback must continue uninterrupted.
  • Log a warning in the console.
  • Offer the viewer an in-player menu to toggle alternative subtitle providers or upload a local .srt/.vtt file.