From 158c26240564418e51af0aa682d9dc87924e0faa Mon Sep 17 00:00:00 2001 From: Kyle Seager Date: Wed, 19 Nov 2025 10:11:11 -0700 Subject: [PATCH 1/3] added ability to override error-controller error handling via HlsConfig --- docs/API.md | 66 ++++++++++++++++++++++++++++++ src/config.ts | 7 ++++ src/controller/error-controller.ts | 10 ++++- 3 files changed, 81 insertions(+), 2 deletions(-) diff --git a/docs/API.md b/docs/API.md index 0dc1e7779b0..f7f0eb60cdb 100644 --- a/docs/API.md +++ b/docs/API.md @@ -96,6 +96,8 @@ See [API Reference](https://hlsjs-dev.video-dev.org/api-docs/) for a complete li - [`capLevelController`](#caplevelcontroller) - [`fpsController`](#fpscontroller) - [`errorController`](#errorcontroller) + - [`onErrorHandler`](#onerrorhandler) + - [`onErrorOutHandler`](#onerrorouthandler) - [`timelineController`](#timelinecontroller) - [`enableDateRangeMetadataCues`](#enabledaterangemetadatacues) - [`enableEmsgMetadataCues`](#enableemsgmetadatacues) @@ -1393,6 +1395,70 @@ Customized error controller. A class in charge of handling errors and error recovery logic. The error controller processes error events and implements recovery strategies such as level switching and fragment retry logic. +### `onErrorHandler` + +(default: `undefined`, type: `(data: ErrorData) => boolean`) + +Early error handler callback invoked **before** HLS.js attempts any error recovery. + +This callback is called immediately when an error occurs, giving you the earliest opportunity to inspect and handle errors. When you return `true`, HLS.js will **not attempt any error recovery** and you are responsible for handling the error manually. Return `false` or `undefined` to allow HLS.js to proceed with its normal error recovery logic. + +**Important:** Returning `true` prevents all error recovery attempts including fragment retries, level switching, and other recovery strategies. + +```js +var config = { + onErrorHandler: function (data) { + console.log( + 'Error handler called before recovery:', + data.type, + data.details, + ); + + // Example: Handle specific errors manually + if (data.details === Hls.ErrorDetails.FRAG_LOAD_ERROR) { + // Custom handling logic here + console.log('Manually handling fragment load error'); + return true; // Prevent HLS.js from attempting recovery + } + + // Let HLS.js handle other errors + return false; + }, +}; +``` + +### `onErrorOutHandler` + +(default: `undefined`, type: `(data: ErrorData) => boolean`) + +Error handler callback invoked **after** HLS.js has completed its error recovery attempts. + +This callback is called after HLS.js has exhausted its recovery strategies (such as retries and level switching). When you return `true`, HLS.js will **not execute its default error handling** and you are responsible for handling the error manually. Return `false` or `undefined` to allow HLS.js to proceed with its default error handling (which may include destroying the player or switching streams). + +**Important:** Returning `true` prevents default error handling after recovery has been attempted. This is useful for implementing custom fallback logic when HLS.js cannot recover from an error. + +```js +var config = { + onErrorOutHandler: function (data) { + console.log( + 'Error handler called after recovery:', + data.type, + data.details, + ); + + // Example: Custom fallback for unrecoverable errors + if (data.fatal && data.type === Hls.ErrorTypes.NETWORK_ERROR) { + // Custom handling logic here + console.log('Manually handling fragment load error'); + return true; // Prevent HLS.js default handling + } + + // Let HLS.js handle other errors with its default behavior + return false; + }, +}; +``` + ### `timelineController` (default: internal track timeline controller) diff --git a/src/config.ts b/src/config.ts index 15627fd4a7d..12e3ed87ead 100644 --- a/src/config.ts +++ b/src/config.ts @@ -19,6 +19,7 @@ import { stringify } from './utils/safe-json-stringify'; import XhrLoader from './utils/xhr-loader'; import type { MediaKeySessionContext } from './controller/eme-controller'; import type Hls from './hls'; +import type { ErrorData } from './hls'; import type { FragmentLoaderContext, Loader, @@ -125,6 +126,11 @@ export type EMEControllerConfig = { requireKeySystemAccessOnStart: boolean; }; +export type ErrorControllerConfig = { + onErrorHandler?: (data: ErrorData) => boolean; + onErrorOutHandler?: (data: ErrorData) => boolean; +}; + export interface FragmentLoaderConstructor { new (confg: HlsConfig): Loader; } @@ -335,6 +341,7 @@ export type HlsConfig = { BufferControllerConfig & CapLevelControllerConfig & EMEControllerConfig & + ErrorControllerConfig & FPSControllerConfig & GapControllerConfig & LevelControllerConfig & diff --git a/src/controller/error-controller.ts b/src/controller/error-controller.ts index 2650db155eb..139d70f9a49 100644 --- a/src/controller/error-controller.ts +++ b/src/controller/error-controller.ts @@ -134,10 +134,11 @@ export default class ErrorController } private onError(event: Events.ERROR, data: ErrorData) { - if (data.fatal) { + const hls = this.hls; + // If the error is handled by onErrorHandler or is fatal, do not proceed with error recovery + if (hls.config.onErrorHandler?.(data) || data.fatal) { return; } - const hls = this.hls; const context = data.context; switch (data.details) { @@ -465,6 +466,11 @@ export default class ErrorController } public onErrorOut(event: Events.ERROR, data: ErrorData) { + const hls = this.hls; + // If the error is handled by onErrorOutHandler, do not proceed with the default error handling + if (hls.config.onErrorOutHandler?.(data)) { + return; + } switch (data.errorAction?.action) { case NetworkErrorAction.DoNothing: break; From 309e2121007029f4ebf48243d6412088b7034bd2 Mon Sep 17 00:00:00 2001 From: Kyle Seager Date: Wed, 19 Nov 2025 10:23:06 -0700 Subject: [PATCH 2/3] Updated docs & export --- api-extractor/report/hls.js.api.md | 10 +++++++++- src/hls.ts | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/api-extractor/report/hls.js.api.md b/api-extractor/report/hls.js.api.md index cda617f5ca0..da3dcf8c5e8 100644 --- a/api-extractor/report/hls.js.api.md +++ b/api-extractor/report/hls.js.api.md @@ -1230,6 +1230,14 @@ export class ErrorController extends Logger implements NetworkComponentAPI { stopLoad(): void; } +// Warning: (ae-missing-release-tag) "ErrorControllerConfig" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ErrorControllerConfig = { + onErrorHandler?: (data: ErrorData) => boolean; + onErrorOutHandler?: (data: ErrorData) => boolean; +}; + // Warning: (ae-missing-release-tag) "ErrorData" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -2297,7 +2305,7 @@ export type HlsConfig = { progressive: boolean; lowLatencyMode: boolean; primarySessionId?: string; -} & ABRControllerConfig & BufferControllerConfig & CapLevelControllerConfig & EMEControllerConfig & FPSControllerConfig & GapControllerConfig & LevelControllerConfig & MP4RemuxerConfig & StreamControllerConfig & SelectionPreferences & LatencyControllerConfig & MetadataControllerConfig & TimelineControllerConfig & TSDemuxerConfig & HlsLoadPolicies & FragmentLoaderConfig & PlaylistLoaderConfig; +} & ABRControllerConfig & BufferControllerConfig & CapLevelControllerConfig & EMEControllerConfig & ErrorControllerConfig & FPSControllerConfig & GapControllerConfig & LevelControllerConfig & MP4RemuxerConfig & StreamControllerConfig & SelectionPreferences & LatencyControllerConfig & MetadataControllerConfig & TimelineControllerConfig & TSDemuxerConfig & HlsLoadPolicies & FragmentLoaderConfig & PlaylistLoaderConfig; // Warning: (ae-missing-release-tag) "HlsEventEmitter" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // diff --git a/src/hls.ts b/src/hls.ts index cf61a67b01a..28016e6ca8e 100644 --- a/src/hls.ts +++ b/src/hls.ts @@ -1299,6 +1299,7 @@ export type { CapLevelControllerConfig, CMCDControllerConfig, EMEControllerConfig, + ErrorControllerConfig, DRMSystemConfiguration, DRMSystemsConfiguration, DRMSystemOptions, From 5c949e3e6666d3a5b445cee7d55046fd12b96f23 Mon Sep 17 00:00:00 2001 From: Kyle Seager Date: Wed, 19 Nov 2025 12:34:52 -0700 Subject: [PATCH 3/3] addressed PR comments --- src/controller/error-controller.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/controller/error-controller.ts b/src/controller/error-controller.ts index 139d70f9a49..42e7167ed2c 100644 --- a/src/controller/error-controller.ts +++ b/src/controller/error-controller.ts @@ -137,6 +137,9 @@ export default class ErrorController const hls = this.hls; // If the error is handled by onErrorHandler or is fatal, do not proceed with error recovery if (hls.config.onErrorHandler?.(data) || data.fatal) { + if (data.errorAction) { + data.errorAction.resolved = true; + } return; } const context = data.context; @@ -469,6 +472,9 @@ export default class ErrorController const hls = this.hls; // If the error is handled by onErrorOutHandler, do not proceed with the default error handling if (hls.config.onErrorOutHandler?.(data)) { + if (data.errorAction) { + data.errorAction.resolved = true; + } return; } switch (data.errorAction?.action) {