# 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 ```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.