BabylonJS/Babylon.js · error · Error

Unable to generate navMesh: ${e}

Error message

Unable to generate navMesh: ${e}

What it means

The worker's onmessage handler in GenerateNavMeshWithWorker() (generator.worker.ts:47) throws when the worker reports success === false, embedding the whole event object in the message. This means generation failed inside the worker — the same class of failure as error 12 (Recast rejected the geometry or config) — but surfaced asynchronously through the message channel. Note the thrown error is thrown inside the onmessage callback, so it lands in the worker message handler's context, not the caller's await chain.

Source

Thrown at packages/dev/addons/src/navigation/generator/generator.worker.ts:47

         * @param navMesh The generated NavMesh.
         * @param navMeshQuery The NavMeshQuery associated with the generated NavMesh.
         * @param tileCache Optional TileCache if tile cache generation was used.
         */
        completion: (navMesh: NavMesh, navMeshQuery: NavMeshQuery, tileCache?: TileCache) => void;
        /**
         *  Worker instance used for asynchronous NavMesh generation.
         */
        worker: Worker;
    }
) {
    if (meshes.length === 0) {
        throw new Error("At least one mesh is needed to create the nav mesh.");
    }

    // callback function to process the message from the worker
    workerOptions.worker.onmessage = (e) => {
        if ((e as any).data?.success === false) {
            throw new Error(`Unable to generate navMesh: ${e}`);
        } else {
            const { navMesh, tileCache } = e.data;
            if (tileCache) {
                // if tileCache is present, the binary data contains the navmesh and the tilecache as well
                const tileCacheArray = new Uint8Array(tileCache);
                const navMeshData = BuildFromTileCacheData(tileCacheArray, CreateDefaultTileCacheMeshProcess());
                workerOptions.completion(navMeshData.navMesh, navMeshData.navMeshQuery, navMeshData.tileCache);
                return;
            } else {
                if (navMesh) {
                    // deserialize the navmesh only (no tilecache present)
                    const navMeshArray = new Uint8Array(navMesh);
                    const navMeshData = BuildFromNavmeshData(navMeshArray);
                    workerOptions.completion(navMeshData.navMesh, navMeshData.navMeshQuery, undefined);
                    return;
                }
            }

View on GitHub (pinned to 0592b347b8)

Solutions

  1. Inspect e.data.error (or stringify the event) to get the worker-side Recast failure reason and fix that specific issue
  2. Match the fixes from the single-threaded path: correct doNotReverseIndices winding, tileSize 32–64 with maxObstacles > 0, sane config values for your scene scale
  3. Reduce scene complexity/geometry size if the worker hits WASM memory limits; generate fewer tiles or smaller meshes
  4. Wrap createNavMeshAsync in try/catch AND note the throw occurs in onmessage — ensure your plugin surfaces worker failures as promise rejections you can catch

Example fix

// before
workerOptions.worker.onmessage = (e) => {
    if ((e as any).data?.success === false) {
        throw new Error(`Unable to generate navMesh: ${e}`); // opaque event
    }
};

// after (diagnosing)
workerOptions.worker.onmessage = (e) => {
    if ((e as any).data?.success === false) {
        console.error("Worker navmesh failure:", (e as any).data?.error);
        // fix config, e.g.:
        // parameters.tileSize = 64; parameters.doNotReverseIndices = false;
    }
};
Defensive patterns

Strategy: try-catch

Validate before calling

workerOptions.worker.onerror = (err) => console.error("navmesh worker error:", err);
workerOptions.worker.onmessage = (e) => {
    if ((e as any).data?.success === false) {
        console.error("Recast failure detail:", (e as any).data?.error);
    }
};
// validate parameters before sending to the worker
if ((parameters.maxObstacles ?? 0) > 0 && ((parameters.tileSize ?? 0) < 32 || (parameters.tileSize ?? 0) > 64)) {
    throw new Error("tileSize must be 32-64 with maxObstacles > 0");
}

Type guard

function isWorkerFailure(e: MessageEvent): e is MessageEvent & { data: { success: false; error: string } } {
    return (e as any).data?.success === false && typeof (e as any).data?.error === "string";
}

Try / catch

try {
    await plugin.createNavMeshAsync(meshes, parameters);
} catch (e) {
    if (e instanceof Error && e.message.includes("Unable to generate navMesh")) {
        console.error("Worker-side generation failed; check config/winding/geometry", e.message);
        // retry with corrected parameters or fall back to single-threaded generation
    }
}

Prevention

When it happens

Trigger: Worker-side Recast generation returns success:false: invalid config (bad cellSize/agent values, tileSize outside 32–64 with maxObstacles), wrong triangle winding (doNotReverseIndices mismatch), degenerate geometry, or geometry incompatible with tile bounds. Any worker-side error, including WASM memory failures, is reported via this message.

Common situations: Large scenes blowing the worker's WASM memory limits during generation; config copied from a single-threaded setup that doesn't suit the tiled/tile-cache path; winding flipped by an exporter; diagnosing is harder because the message contains the event object rather than a clean error string.

Related errors


AI-assisted analysis of BabylonJS/Babylon.js@0592b347b8 (2026-08-30). Data as JSON: /api/errors/22e80c002f14558b. Report an issue: GitHub.