Skip to content

GuidePlayback

Handle playback errors

Display playback failures, report them, and choose a useful recovery action.

Display playback failures instead of leaving a dead player, report the failure when you need diagnostics, and give users a useful next step.

Render a <media-error-dialog> inside the player container. It reads error state, presents the failure, and dismisses cleanly. The packaged skins include one.

<video-player>
  <media-container>
    <video src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4" muted playsinline></video>
    <media-error-dialog>
      <media-dialog-popup>
        <media-dialog-title></media-dialog-title>
        <media-dialog-description></media-dialog-description>
        <media-dialog-close></media-dialog-close>
      </media-dialog-popup>
    </media-error-dialog>
  </media-container>
</video-player>
<script type="module">
  import '@videojs/html/video/player';
  import '@videojs/html/ui/container';
  import '@videojs/html/ui/error-dialog';
  import '@videojs/html/ui/dialog-popup';
  import '@videojs/html/ui/dialog-title';
  import '@videojs/html/ui/dialog-description';
  import '@videojs/html/ui/dialog-close';
</script>

The dialog shows its text through child parts: a title, the error description, and a dismiss button. Each empty part gets default copy. Write your own text inside a part to replace it. Without the child parts, the dialog opens empty.

How it works

The error feature mirrors the media component’s error into player state:

  • error holds the media’s error — code, message — or null when there is none.
  • dismissError() clears the error from player state (the media component keeps its own error until a new load).
  • Loading a new source resets error to null automatically.

The dialog owns presentation, not recovery. Decide the next step in your application: let the user dismiss the message, choose another item, refresh an expired URL, or contact support.

Availability and constraints

  • For a terminal failure, load a new or refreshed source to recover. An aborted load or an adapter-specific error may have different behavior.
  • Standard media error codes distinguish broad classes — aborted, network, decode, and source-not-supported — rather than root causes. A media adapter can report its own numeric code too.
  • Streaming engines handle recoverable segment and network failures before surfacing a terminal error. Do not layer a generic same-source retry loop on top of the engine.
  • The feature records error events after attachment. An error already present on the media is not copied into state. Keep the error surface mounted regardless of playback state.

A src in page markup can start loading before the player script runs. To capture startup failures, omit src from the markup and assign it after you import the player and it attaches to the media. Check player.store.target to confirm attachment; if it is absent, wait for a store notification. When adopting an already loaded element, check video.error once and show an application message if an error is present.

Common variations

Offer an application-specific next step

Use the error code and your product context to choose the action. A live event can link to its status page, an authenticated app can refresh an expired playback URL, and a playlist can offer the next item. Keep that action outside <media-error-dialog>; the dialog can close after your application has handled the error.

Report errors to monitoring

Subscribe to error state and forward new errors to your monitoring service with the source URL and error.code. A media error does not automatically appear in JavaScript exception monitoring.

Subscribe after the player element has registered. Compare the current error with the previous one because the store also notifies for unrelated state changes:

import '@videojs/html/video/player';
import { selectError } from '@videojs/html';

const player = document.querySelector('video-player');
if (!player) throw new Error('Missing video-player');

const store = player.store;
let previousError: { code: number; message: string } | null = null;

function reportNewError() {
  const error = selectError(store.state)?.error ?? null;
  if (error === previousError) return;

  previousError = error;
  if (error) console.error('Playback failed', error.code, error.message);
}

const unsubscribe = store.subscribe(reportNewError);
reportNewError();
// Call unsubscribe() when this player is removed.

Replace the console.error call with your monitoring service and include the current source URL.

Diagnose a reported failure

Start with error.code and the affected source URL. Check the browser’s network panel for failed media requests, response status, redirects, CORS headers, and expired credentials. If requests succeed, check the file’s container and codecs against the chosen media component. For HLS or DASH, inspect the streaming engine’s diagnostics for segment and manifest failures; the player error often names only the final outcome.

Troubleshooting

The player is black and silent with no error

Not every dead player is a media error — autoplay blocking and an unset source produce no error. Check error, source, and paused in player state to tell them apart. If play() rejects with NotAllowedError, see Autoplay.

A VJS8_LEGACY_* exception appears

These codes identify Video.js 8 APIs used with the Video.js 10 video.js package. Look up the code in the error code reference for its trigger and v10 equivalent. These exceptions are separate from the player’s error state, which receives media playback errors after setup.

Error code says the source isn’t supported

The browser can’t play the format through this media component. HLS in a plain video element outside Safari is the classic case; use the matching streaming media component. See Media sources.

Reopening the source produces the same error

The failure is probably permanent for that source, such as a 404, revoked token, unsupported codec, or CORS response. Fix or replace the source instead of repeatedly loading the same URL.

Components

API

Guides