Skip to content

Errors

The player uses a structured error hierarchy. All errors carry a string code, a severity, and a human-readable message. Catching by code or by class is always preferred over catching raw Error.

Error classes

ClassCode prefixWhen
PlayerErrorcore:*Base class. General player errors.
NotImplementedErrorcore:not-implemented/*Method exists in the contract but is not implemented by the current backend or library
NetworkErrorcore:network/*HTTP or connectivity failures
AuthErrorcore:auth/*Authentication and authorization failures
MediaFormatErrorcore:media/*Unsupported or malformed media format
StreamErrorcore:stream/*HLS/DASH stream errors
ResourceErrorcore:resource/*Missing or inaccessible resources
DrmErrorcore:drm/*DRM/EME failures
BrowserPolicyErrorcore:policy/*Browser autoplay or permissions policy blocks
PluginErrorplugin:*Errors originating inside a plugin

All classes are exported from @nomercy-entertainment/nomercy-player-core@beta.

Error codes

Code format: <vendor>:<category>/<detail>.

Core codes always start with core:. Plugin codes are namespaced with the plugin id, e.g. equalizer:band/out-of-range.

Auth errors (AuthError)

CodeStatusWhen
core:auth/forbidden403Access denied. Never retried.
core:auth/unauthenticated401Token missing or expired and refresh failed.
core:auth/refresh-failed401refreshOnUnauthenticated threw or returned no token.

Network errors (NetworkError)

CodeWhen
core:network/timeoutRequest exceeded timeoutMs.
core:network/request-timeoutServer returned 408.
core:network/abortedRequest was aborted (e.g. on plugin dispose).
core:network/offlinenavigator.onLine is false when a request is made.
core:network/not-found404.
core:network/gone410.
core:network/rate-limited429.
core:network/client-errorOther 4xx.
core:network/server-error500.
core:network/bad-gateway502.
core:network/service-unavailable503.
core:network/gateway-timeout504.
core:network/server-error-otherOther 5xx.
core:network/parse-failedResponse body could not be parsed.

Stream errors (StreamError)

CodeWhen
core:stream/fragment-failedAn HLS fragment/segment failed to load or parse.
core:stream/hls-attach-failedhls.js could not attach to the media element.
core:stream/no-factory-matchNo registered stream factory matched the source.

Media format errors (MediaFormatError)

CodeWhen
core:media/codec-unsupportedBrowser rejects the codec string via canPlayType.
core:media/hls-unsupportedHLS source but no native or hls.js support.
core:media/load-failedThe media element failed to load the source.
core:media/missing-urlPlaylist item has no resolvable url.

Resource errors (ResourceError)

CodeWhen
core:resource/worker-init-failedA Web Worker could not be started (e.g. the OctopusPlugin worker).

Browser policy errors (BrowserPolicyError)

CodeWhen
core:policy/autoplay-blockedBrowser blocked play() due to autoplay policy.
core:policy/fullscreenUnsupportedFullscreen requested but not available.
core:policy/pipUnsupportedPicture-in-picture requested but not available.
core:policy/emeUnsupportedEME/DRM requested but not available.
core:policy/castUnavailableCast requested but the Cast SDK is unavailable.

There is a core:policy/<capability>Unsupported (or Unavailable) code for each gated browser capability — AirPlay, Web Audio, canvas 2D, capture-stream, IndexedDB, remote playback, wake lock, setSinkId, and the audio-output picker among them.

Playlist errors (PlayerError)

CodeWhen
core:playlist/fetch-errorURL-based playlist fetch failed.
core:playlist/parse-errorResponse is not a valid JSON array.

Plugin errors (PluginError)

CodeWhen
core:plugin/missing-depA required plugin dependency is not registered.
core:plugin/version-mismatchA dependency plugin version is below minVersion.
core:plugin/duplicate-idA plugin with the same id is already registered (and replaces is not set).
core:plugin/init-timeoutuse() did not resolve within pluginInitTimeoutMs.

Not-implemented errors (NotImplementedError)

CodeWhen
core:not-implemented/subtitlessubtitles() called on NMMusicPlayer.
core:not-implemented/<feature>Any structural API stub not supported by the current backend.

Severity tiers

SeverityMeaning
'fatal'Playback cannot continue. Player stops and emits fatal. Plugin marks itself failed.
'error'Operation failed. Plugin keeps running. error event fires.
'warning'Degraded behavior. Plugin survives and emits warning.
'info'Observability, no impact on behavior.

Listening to errors

TypeScript
player.on('error', (event) => {
console.error(`Error ${event.error.code}: ${event.error.message}`);
});

player.on('fatal', () => {
showUserMessage('Playback failed. Please reload.');
});

player.on('warning', (event) => {
console.warn(`Warning ${event.error.code}: ${event.error.message}`);
});

The handler receives a PlayerErrorEvent. The structured error lives on .error (so it is event.error.code, not event.code); the rest of the payload is the severity, scope, timestamp, and the cancellable-event controls:

TypeScript
interface PlayerErrorEvent {
error: PlayerError; // .code, .message, .cause?, .context?, .suggestion?
severity: 'fatal' | 'error' | 'warning' | 'info';
scope: ErrorScope;
timestamp: number;

markHandled(): void;
isHandled(): boolean;
stopImmediatePropagation(): void;
isPropagationStopped(): boolean;
preventDefault(): void; // suppress the kit's default action (e.g. auto-retry)
isDefaultPrevented(): boolean;
}

NotImplementedError

NotImplementedError is thrown when a method exists in the public contract but the current backend or library does not support it.

Known cases in v2.0:

  • player.subtitles() on NMMusicPlayer: audio backends have no subtitle tracks
  • GroupListeningPlugin, DrmPlugin, LiveTranscodingPlugin on NMMusicPlayer: these throw NotImplementedError in their use() (planned for v2.1)

Catch by class, not by message:

TypeScript
import { NotImplementedError } from '@nomercy-entertainment/nomercy-player-core';

try {
const tracks = player.subtitles();
} catch (error) {
if (error instanceof NotImplementedError) {
// expected: audio player doesn't have subtitle tracks
} else {
throw error; // re-throw unexpected errors
}
}

Code always follows core:not-implemented/<feature>.

Static recovery policy

Plugins declare how they respond to specific error codes:

TypeScript
class MyPlugin extends Plugin {
static readonly onError: Record<string, PluginRecoveryAction> = {
'core:stream/segment-load-failed': 'retry-once',
'core:auth/forbidden': 'disable',
'core:network/timeout': 'ignore',
};
}
ActionBehavior
'retry-once'Retry the failing operation once
'fallback'Activate the plugin’s fallback behavior (plugin must implement activateFallback())
'disable'Disable the plugin gracefully
'ignore'Log and continue, no action taken

Recovery actions are declared per plugin through static onError; 'retry-once' calls the plugin’s retryLastOperation() and 'fallback' calls its activateFallback(). There is no setup-level override.

Inside plugins: this.throw and this.report

Never throw raw errors or emit error events directly from a plugin. Always use the plugin’s methods:

TypeScript
// Fatal: plugin fails. Player and other plugins continue.
this.throw({
severity: 'fatal',
code: 'nomercy:sync/connection-lost',
message: 'Lost connection to sync server',
});

// Non-fatal: operation fails. Plugin keeps running.
this.throw({
severity: 'error',
code: 'nomercy:sync/fetch-failed',
message: 'Sync fetch returned an error',
});

// Warning / info: observability. No action needed.
this.report({
severity: 'warning',
code: 'nomercy:sync/rate-limited',
message: 'Sync endpoint rate-limited, will retry in 30s',
});

Errors thrown with this.throw are caught, wrapped in PluginError, emitted via the player’s event bus, and if severity is 'fatal', the plugin is marked failed. Other plugins always continue.