Handle Asset Loading Errors with Three.js LoadingManager

Managing asset loading in Three.js is crucial for creating seamless 3D web experiences. This article explains how to use the THREE.LoadingManager class, specifically focusing on its onError callback, to intercept, identify, and handle individual asset loading errors effectively.

Understanding the LoadingManager

In Three.js, LoadingManager handles and keeps track of loaded and pending data. When you load multiple assets—such as textures, 3D models, or audio files—passing a shared LoadingManager instance to your loaders allows you to monitor global loading progress, completion, and failures.

The onError callback of the LoadingManager triggers whenever any loader associated with that manager encounters an error (for example, a 404 Not Found error or a corrupted file).

Implementing the onError Callback

To intercept asset-specific errors, define the onError function on your LoadingManager instance. This callback receives the URL of the asset that failed to load as its argument.

Here is a practical implementation:

import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';

// 1. Initialize the LoadingManager
const loadingManager = new THREE.LoadingManager();

// 2. Define the onError callback
loadingManager.onError = function ( url ) {
    console.warn( `Error loading asset: ${url}` );
    handleSpecificError( url );
};

// 3. Pass the manager to your loaders
const textureLoader = new THREE.TextureLoader( loadingManager );
const gltfLoader = new GLTFLoader( loadingManager );

// Start loading assets
textureLoader.load( 'textures/wood_diffuse.jpg', (texture) => { /* ... */ } );
gltfLoader.load( 'models/hero_character.gltf', (gltf) => { /* ... */ } );

Intercepting and Handling Specific Assets

Because the onError callback provides the url of the failed resource, you can use conditional logic to determine which asset failed and apply targeted recovery or fallback strategies.

1. Providing Fallback Textures

If a non-critical texture fails to load, you can catch the error and apply a solid color or a default placeholder texture so the 3D model does not render completely black.

function handleSpecificError( url ) {
    if ( url.includes( 'wood_diffuse.jpg' ) ) {
        console.log( 'Falling back to default grey texture.' );
        // Code to apply a default THREE.Color or a local embedded canvas texture
    }
}

2. Handling Critical Model Failures

If a critical asset (like the main character model or environment) fails to load, the application may become unusable. You can use the interceptor to notify the UI layer and display a user-friendly error message.

function handleSpecificError( url ) {
    if ( url.endsWith( '.gltf' ) || url.endsWith( '.glb' ) ) {
        // Show an error overlay to the user
        const uiOverlay = document.getElementById( 'error-overlay' );
        if ( uiOverlay ) {
            uiOverlay.innerText = 'Failed to load 3D models. Please refresh the page.';
            uiOverlay.style.display = 'block';
        }
    }
}

3. Implementing Retry Logic

You can also use the intercepted URL to attempt a reload of the failed asset a limited number of times before failing completely.

const retryCount = {};

function handleSpecificError( url ) {
    if ( !retryCount[url] ) {
        retryCount[url] = 0;
    }

    if ( retryCount[url] < 3 ) {
        retryCount[url]++;
        console.log( `Retrying load for: ${url} (Attempt ${retryCount[url]})` );
        
        // Re-trigger the load depending on the file type
        if ( url.endsWith( '.jpg' ) || url.endsWith( '.png' ) ) {
            textureLoader.load( url, (texture) => { /* Success on retry */ } );
        }
    } else {
        console.error( `Failed to load ${url} after 3 attempts.` );
    }
}