How to Handle Three.js Asset Loading Errors

When building 3D applications with Three.js, failed asset loads can break the user experience or leave users staring at a blank canvas. This article explains how to intercept loading errors using Three.js loaders and the LoadingManager, and outlines practical strategies for displaying fallback 3D graphics and textures when assets fail to load.

Using LoadingManager for Global Error Handling

The THREE.LoadingManager is the most efficient way to monitor all loading events globally. It tracks the progress of all loaders assigned to it and triggers a central error callback if any asset fails to load.

import * as THREE from 'three';

// Create a loading manager
const loadingManager = new THREE.LoadingManager();

// Triggered when all assets are loaded
loadingManager.onLoad = () => {
    console.log('All assets loaded successfully.');
};

// Triggered when an error occurs during loading
loadingManager.onError = (url) => {
    console.error(`There was an error loading: ${url}`);
    // Trigger global fallback state here (e.g., UI notifications)
};

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

Handling Errors on Individual Loaders

If you need specific fallbacks for individual assets, use the error callback parameter within the .load() method of your specific loader. The standard signature for .load() is:

load( url, onLoad, onProgress, onError )

const textureLoader = new THREE.TextureLoader();

textureLoader.load(
    'path/to/texture.jpg',
    (texture) => {
        // Success: Apply texture to material
        material.map = texture;
        material.needsUpdate = true;
    },
    undefined, // Progress callback (optional)
    (error) => {
        // Error: Load fallback texture instead
        console.warn('Texture failed to load. Applying fallback.');
        material.map = createFallbackTexture();
        material.needsUpdate = true;
    }
);

Creating Fallback Graphics

When an asset fails, you should immediately substitute it with a lightweight, procedurally generated graphic so your application remains functional.

1. Generating a Fallback Texture

Instead of loading another external image file (which could also fail), generate a fallback texture dynamically using a HTML canvas or a solid color.

function createFallbackTexture() {
    const canvas = document.createElement('canvas');
    canvas.width = 128;
    canvas.height = 128;
    const ctx = canvas.getContext('2d');

    // Draw a placeholder pattern (e.g., a magenta and black grid)
    ctx.fillStyle = '#ff00ff';
    ctx.fillRect(0, 0, 128, 128);
    ctx.fillStyle = '#000000';
    ctx.fillRect(0, 0, 64, 64);
    ctx.fillRect(64, 64, 64, 64);

    const texture = new THREE.CanvasTexture(canvas);
    return texture;
}

2. Rendering a Fallback 3D Model

If a complex 3D model (like a GLTF/GLB file) fails to load, you should add a simple, lightweight 3D primitive to the scene in its place. This keeps the spatial layout of your scene intact and prevents application-breaking reference errors.

const scene = new THREE.Scene();
const gltfLoader = new GLTFLoader();

gltfLoader.load(
    'models/spaceship.gltf',
    (gltf) => {
        scene.add(gltf.scene);
    },
    undefined,
    (error) => {
        console.error('Failed to load 3D model. Loading fallback geometry.');
        
        // Create a simple wireframe box placeholder
        const fallbackGeometry = new THREE.BoxGeometry(2, 2, 2);
        const fallbackMaterial = new THREE.MeshBasicMaterial({ 
            color: 0xff0000, 
            wireframe: true 
        });
        const fallbackMesh = new THREE.Mesh(fallbackGeometry, fallbackMaterial);
        
        // Position the fallback mesh where the original model was supposed to go
        fallbackMesh.position.set(0, 0, 0);
        scene.add(fallbackMesh);
    }
);