{"record":{"id":"fa30ca8cfbd29eeb","repo":"BabylonJS/Babylon.js","slug":"the","errorCode":null,"errorMessage":"The ","messagePattern":"The ","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/dev/core/src/Meshes/mesh.vertexData.ts","lineNumber":1285,"sourceCode":"                for (let i = 0; i < vertexData.length; i++) {\r\n                    ret[offset + i] = vertexData[i];\r\n                }\r\n                transform && transformRange(ret, transform, offset, vertexData.length);\r\n                offset += vertexData.length;\r\n            }\r\n            return ret;\r\n        }\r\n    }\r\n\r\n    private _validate(): void {\r\n        if (!this.positions) {\r\n            throw new RuntimeError(\"Positions are required\", ErrorCodes.MeshInvalidPositionsError);\r\n        }\r\n\r\n        const getElementCount = (kind: string, values: FloatArray) => {\r\n            const stride = VertexBufferDeduceStride(kind);\r\n            if (values.length % stride !== 0) {\r\n                throw new Error(\"The \" + kind + \"s array count must be a multiple of \" + stride);\r\n            }\r\n\r\n            return values.length / stride;\r\n        };\r\n\r\n        const positionsElementCount = getElementCount(VertexBuffer.PositionKind, this.positions);\r\n\r\n        const validateElementCount = (kind: string, values: FloatArray) => {\r\n            const elementCount = getElementCount(kind, values);\r\n            if (elementCount !== positionsElementCount) {\r\n                throw new Error(\"The \" + kind + \"s element count (\" + elementCount + \") does not match the positions count (\" + positionsElementCount + \")\");\r\n            }\r\n        };\r\n\r\n        if (this.normals) {\r\n            validateElementCount(VertexBuffer.NormalKind, this.normals);\r\n        }\r\n        if (this.tangents) {\r","sourceCodeStart":1267,"sourceCodeEnd":1303,"githubUrl":"https://github.com/BabylonJS/Babylon.js/blob/0592b347b8a4ee0236089ea86a749cacfdb266d8/packages/dev/core/src/Meshes/mesh.vertexData.ts#L1267-L1303","documentation":"VertexData._validate checks that each vertex attribute array (normals, uvs, tangents, colors, etc.) has a length that is an exact multiple of the attribute's per-vertex stride computed by VertexBufferDeduceStride (e.g. 3 for normals, 2 for UVs). A non-multiple means per-vertex reads would be misaligned, so Babylon.js refuses to proceed. This fires inside getElementCount while merging VertexData via VertexData.merge or Mesh.mergeMeshes.","triggerScenarios":"Calling VertexData.merge() (directly or via Mesh.mergeMeshes) where one source VertexData has an attribute array whose length is not divisible by that kind's stride — e.g. a normals array of length 1000 (not divisible by 3) or a UV array of odd length (not divisible by 2).","commonSituations":"Hand-built vertex data with a copy/paste size typo; slicing an attribute array (normals.slice(0, 1000)) without slicing positions to match; loading attribute buffers from external files with padding or trailing garbage; concatenating attributes from meshes with different vertex counts.","solutions":["Fix the offending attribute so its length is a multiple of the expected stride (3 for positions/normals/tangents, 2 for uvs, 4 for colors) — the kind is named in the full error message.","Trim the array to Math.floor(values.length / stride) * stride elements so it aligns per vertex.","Ensure all attributes describe the same vertex count as positions; slice all of them with the same vertex range.","Re-export/re-generate the source data to rule out padding or trailing bytes from an external file or buffer.","Validate each input VertexData individually (check positions.length/3 vs each attribute length/stride) before merging."],"exampleFix":"// before\nconst vertexData = new VertexData();\nvertexData.positions = positions;            // 900 floats (300 vertices)\nvertexData.normals = normals.slice(0, 1000); // 1000 floats -> throws\n// after\nvertexData.normals = normals.slice(0, 900);  // 900 = 300 * 3","handlingStrategy":"validation","validationCode":"const STRIDES = { positions: 3, normals: 3, uvs: 2, uvs2: 2, tangents: 3, colors: 4 };\nfunction validateVertexData(vd) {\n  for (const [kind, stride] of Object.entries(STRIDES)) {\n    const arr = vd[kind];\n    if (arr && arr.length % stride !== 0) {\n      throw new Error(kind + '.length (' + arr.length + ') not a multiple of ' + stride);\n    }\n  }\n  return true;\n}\nvalidateVertexData(vertexData);\nconst merged = VertexData.Merge([vd1, vd2]);","typeGuard":"function isStrideAligned(arr, stride) {\n  return arr == null || ((Array.isArray(arr) || arr instanceof Float32Array) && arr.length % stride === 0);\n}","tryCatchPattern":"try {\n  const merged = VertexData.Merge([vd1, vd2]);\n} catch (e) {\n  if (String(e.message).includes('must be a multiple of')) {\n    // identify the misaligned attribute from the message, fix its length, retry\n  } else { throw e; }\n}","preventionTips":["Derive every attribute array from the same vertex range/count as positions.","Check array.length % stride === 0 for each attribute before constructing or merging VertexData.","When slicing attribute buffers, slice by vertex index (index * stride), not arbitrary element counts.","When building data by hand, log positions.length/3 versus each attribute's length/stride.","Validate imported buffers for padding or trailing bytes before use."],"tags":["geometry","vertex-data","validation","merge"],"backgroundTag":"vertex-data-stride-mismatch","analyzedSha":"0592b347b8a4ee0236089ea86a749cacfdb266d8","analyzedAt":"2026-08-30T15:11:20.442Z","schemaVersion":2},"datasetVersion":"2026-08-30T18:17:15.746Z"}