BabylonJS/Babylon.js · error

Unexpected number of color components: ${vertexBuffer.getSiz

Error message

Unexpected number of color components: ${vertexBuffer.getSize()}

What it means

When extracting/serializing vertex data (VertexData.ExtractFromMesh / ExtractFromGeometry), the colors buffer is normalized to 4 components: a 3-component buffer is expanded with alpha=1 and a 4-component buffer is used as-is. Any other component count (1, 2, >4) has no defined RGBA mapping, so it throws.

Source

Thrown at packages/dev/core/src/Meshes/mesh.vertexData.ts:1513

        }

        if (meshOrGeometry.isVerticesDataPresent(VertexBuffer.ColorKind)) {
            const geometry = (meshOrGeometry as Mesh).geometry || (meshOrGeometry as Geometry);
            const vertexBuffer = geometry.getVertexBuffer(VertexBuffer.ColorKind)!;
            const colors = geometry.getVerticesData(VertexBuffer.ColorKind, copyWhenShared, forceCopy)!;
            if (vertexBuffer.getSize() === 3) {
                const newColors = new Float32Array((colors.length * 4) / 3);
                for (let i = 0, j = 0; i < colors.length; i += 3, j += 4) {
                    newColors[j] = colors[i];
                    newColors[j + 1] = colors[i + 1];
                    newColors[j + 2] = colors[i + 2];
                    newColors[j + 3] = 1;
                }
                result.colors = newColors;
            } else if (vertexBuffer.getSize() === 4) {
                result.colors = colors;
            } else {
                throw new Error(`Unexpected number of color components: ${vertexBuffer.getSize()}`);
            }
        }

        if (meshOrGeometry.isVerticesDataPresent(VertexBuffer.MatricesIndicesKind)) {
            result.matricesIndices = meshOrGeometry.getVerticesData(VertexBuffer.MatricesIndicesKind, copyWhenShared, forceCopy);
        }

        if (meshOrGeometry.isVerticesDataPresent(VertexBuffer.MatricesWeightsKind)) {
            result.matricesWeights = meshOrGeometry.getVerticesData(VertexBuffer.MatricesWeightsKind, copyWhenShared, forceCopy);
        }

        if (meshOrGeometry.isVerticesDataPresent(VertexBuffer.MatricesIndicesExtraKind)) {
            result.matricesIndicesExtra = meshOrGeometry.getVerticesData(VertexBuffer.MatricesIndicesExtraKind, copyWhenShared, forceCopy);
        }

        if (meshOrGeometry.isVerticesDataPresent(VertexBuffer.MatricesWeightsExtraKind)) {
            result.matricesWeightsExtra = meshOrGeometry.getVerticesData(VertexBuffer.MatricesWeightsExtraKind, copyWhenShared, forceCopy);
        }

View on GitHub (pinned to 0592b347b8)

Solutions

  1. Fix the colors buffer so each vertex has exactly 4 components (RGBA), or 3 which the extractor will pad with alpha=1.
  2. Rebuild the buffer: allocate new Float32Array(vertexCount*4) and copy/expand each vertex's color to RGBA.
  3. Trace where the mesh got its color data; if a custom loader set it, correct the stride at the source.
  4. Verify components per vertex with mesh.getVerticesData(VertexBuffer.ColorKind).length / vertexCount before extracting.

Example fix

// before
const badColors = new Float32Array(vertexCount * 2); // 2 components
geometry.setVerticesData(VertexBuffer.ColorKind, badColors);
VertexData.ExtractFromMesh(mesh); // throws
// after
const colors = new Float32Array(vertexCount * 4);
for (let i = 0; i < vertexCount; i++) {
  colors[i*4] = r; colors[i*4+1] = g; colors[i*4+2] = b; colors[i*4+3] = 1;
}
geometry.setVerticesData(VertexBuffer.ColorKind, colors);
Defensive patterns

Strategy: validation

Validate before calling

function validateColorBuffer(meshOrGeometry, vertexCount) {
  const colors = meshOrGeometry.getVerticesData(BABYLON.VertexBuffer.ColorKind);
  if (!colors) return true;
  const comps = colors.length / vertexCount;
  if (comps !== 3 && comps !== 4) {
    throw new Error('Colors have ' + comps + ' components/vertex; expected 3 or 4');
  }
  return true;
}
validateColorBuffer(mesh, mesh.getTotalVertices());
const vd = VertexData.ExtractFromMesh(mesh);

Type guard

function hasValidColorComponents(colors, vertexCount) {
  if (!colors) return true;
  const comps = colors.length / vertexCount;
  return comps === 3 || comps === 4;
}

Try / catch

try {
  const vd = VertexData.ExtractFromMesh(mesh);
} catch (e) {
  if (String(e.message).includes('Unexpected number of color components')) {
    // rebuild the colors buffer as RGBA (4 floats per vertex) then retry
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling VertexData.ExtractFromMesh / ExtractFromGeometry (or serialize paths using them) on a mesh or geometry whose VertexBuffer.ColorKind buffer has a size other than 3 or 4 — e.g. a colors array set manually with 2 floats per vertex, or a custom buffer registered under the color kind with an unexpected stride.

Common situations: Hand-assembled color data with the wrong per-vertex float count; importing color attributes from a custom file format and storing them verbatim with a stride other than 3/4; a bug in a custom loader/decoder writing colors with the wrong stride.

Related errors


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