BabylonJS/Babylon.js · error · Error

Draco: Failed to encode.

Error message

Draco: Failed to encode.

What it means

After building the Draco mesh and encoder, EncodeMesh calls encoder.EncodeMeshToDracoBuffer and checks the returned length. A non-positive length means the native Draco encoder produced no output (encoding failed internally), so the worker throws this Error instead of returning an empty buffer.

Source

Thrown at packages/dev/core/src/Meshes/Compression/dracoCompressionWorker.ts:111

            attributeIDs[attribute.kind] = addAttribute(meshBuilder, mesh, encoderModule[attribute.dracoName], verticesCount, attribute.size, attribute.data);
            if (options.quantizationBits && options.quantizationBits[attribute.dracoName]) {
                encoder.SetAttributeQuantization(encoderModule[attribute.dracoName], options.quantizationBits[attribute.dracoName]);
            }
        }

        // Set the options
        if (options.method) {
            encoder.SetEncodingMethod(encoderModule[options.method]);
        }
        if (options.encodeSpeed !== undefined && options.decodeSpeed !== undefined) {
            encoder.SetSpeedOptions(options.encodeSpeed, options.decodeSpeed);
        }

        // Encode to native buffer
        encodedNativeBuffer = new encoderModule.DracoInt8Array();
        const encodedLength = encoder.EncodeMeshToDracoBuffer(mesh, encodedNativeBuffer);
        if (encodedLength <= 0) {
            throw new Error("Draco: Failed to encode.");
        }

        // Copy the native buffer data to worker heap
        const encodedData = new Int8Array(encodedLength);
        for (let i = 0; i < encodedLength; i++) {
            encodedData[i] = encodedNativeBuffer.GetValue(i);
        }

        return { data: encodedData, attributeIds: attributeIDs };
    } finally {
        if (mesh) {
            encoderModule.destroy(mesh);
        }
        if (meshBuilder) {
            encoderModule.destroy(meshBuilder);
        }
        if (encoder) {
            encoderModule.destroy(encoder);

View on GitHub (pinned to 0592b347b8)

Solutions

  1. Validate that every attribute's data length matches numPoints * numComponents and indices are within range before encoding.
  2. Simplify the options (e.g. drop quantization bits settings) to isolate which option breaks encoding.
  3. Check that the Draco encoder module version matches the data/features used (e.g. position quantization support).
  4. Log attribute sizes and index ranges and retry encoding after fixing the mesh data.

Example fix

// before
const mesh = { attributes, indices: badIndices }; // indices reference vertex 999999
encoder.EncodeMeshToDracoBuffer(mesh, out); // <= 0 -> throws

// after
const maxIndex = Math.max(...indices);
if (maxIndex >= positions.length / 3) {
  indices = indices.filter((i) => i < positions.length / 3); // or fix mesh
}
const encodedLength = encoder.EncodeMeshToDracoBuffer(mesh, out);
Defensive patterns

Strategy: validation

Validate before calling

const numPoints = positions.length / 3;
const maxIndex = indices.reduce((m, i) => Math.max(m, i), 0);
if (!Number.isFinite(numPoints) || numPoints === 0) throw new Error('empty position data');
if (maxIndex >= numPoints) throw new Error('index out of range: ' + maxIndex);
for (const a of attributes) {
  if (a.data.length !== numPoints * a.numComponents) throw new Error('attribute size mismatch: ' + a.kind);
}

Type guard

const isEncodableMesh = (m: { attributes: any[]; indices: number[] }): boolean =>
  m.attributes.every((a) => Array.isArray(a.data) || ArrayBuffer.isView(a.data)) &&
  m.indices.every((i) => Number.isInteger(i) && i >= 0);

Try / catch

try {
  return await encodeMeshAsync(attributes, indices, options);
} catch (e) {
  if (String(e?.message).includes('Failed to encode')) {
    return await encodeMeshAsync(attributes, indices, { quantizationBits: undefined }); // retry without exotic options
  }
  throw e;
}

Prevention

When it happens

Trigger: EncodeMeshToDracoBuffer returns <= 0, typically due to invalid/inconsistent attribute data (wrong buffer sizes vs point counts), corrupted indices, or unsupported option combinations passed through options.

Common situations: Manually assembling attribute data with mismatched vertex counts; index buffers referencing vertices beyond the position count; feeding NaN/degenerate data; rare Draco native-library failures on exotic attribute combinations.

Related errors


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