BabylonJS/Babylon.js · error

Cannot rebuild compound part "${proxy.name}": the retained c

Error message

Cannot rebuild compound part "${proxy.name}": the retained compound source data is not available.

What it means

During compound rebuild, for each non-streaming proxy part the mesh tries to recover its retained source data via `_createRetainedPartSource(proxy)`. If that returns null the original splat data for the part is no longer available (e.g. released after merge), so rebuilding the buffers would silently corrupt the compound — hence the throw naming the proxy mesh.

Source

Thrown at packages/dev/core/src/Meshes/GaussianSplatting/gaussianSplattingMesh.pure.ts:1276

                    // scenario B, part 0 is itself a proxied part with no implicit "own" data.
                    for (let partIndex = 0; partIndex < this._partProxies.length; partIndex++) {
                        const proxy = this._partProxies[partIndex];
                        if (!proxy) {
                            continue;
                        }
                        // Streaming parts have no retained CPU source — their atlas rows (core + SH) are
                        // GPU-authoritative and preserved across the rebuild by the backup/restore hooks. Leave their
                        // rows at the fresh-array defaults (zero core, neutral SH) for the restore to overwrite; just
                        // advance the offset. (With a static part present _splatsData is non-null and these would
                        // otherwise reconstruct as a harmless zeros-slice; with only streaming parts it is null and
                        // _createRetainedPartSource returns null — so this skip is required, not just an optimization.)
                        if (this._streamingStates.some((s) => s.proxy === proxy)) {
                            rebuildOffset += proxy._vertexCount;
                            continue;
                        }
                        const source = this._createRetainedPartSource(proxy);
                        if (!source) {
                            throw new Error(`Cannot rebuild compound part "${proxy.name}": the retained compound source data is not available.`);
                        }
                        this._appendPartSourceToArrays(source, rebuildOffset, covA, covB, colorArray, sh, minimum, maximum);
                        rebuildOffset += source._vertexCount;
                    }
                } else {
                    // No proxies yet: this is the very first addPart call on a mesh that loaded
                    // its own splat data (scenario A legacy path). Re-process that own data so
                    // it occupies the start of the new texture before the incoming part is appended.
                    // In the preferred scenario B (empty composer) splatCountA is 0 and this
                    // entire branch is skipped by the outer `if (splatCountA > 0)` guard.
                    if (this._splatsData) {
                        const uBufA = GaussianSplattingMeshBase._GetSplatDataBytes(this._splatsData);
                        const fBufA = GaussianSplattingMeshBase._GetSplatDataFloats(this._splatsData);
                        for (let i = 0; i < splatCountA; i++) {
                            this._makeSplat(i, fBufA, uBufA, covA, covB, colorArray, minimum, maximum, false);
                        }
                        if (sh && this._shData) {
                            for (let texIdx = 0; texIdx < sh.length; texIdx++) {

View on GitHub (pinned to 0592b347b8)

Solutions

  1. Keep the retained source data: don't dispose/release the original part `_splatsData` for meshes used as compound parts.
  2. Avoid streaming-proxy marking mismatches: ensure `_streamingStates` correctly includes streaming proxies so they are skipped, not rebuilt.
  3. Re-load the original part meshes from their source files before rebuilding the compound.
  4. Wrap rebuild in try/catch and fall back to re-creating the compound from freshly loaded sources.

Example fix

// before
compound.dispose(false); // releases retained part sources
// ... later, on context restore
compound.rebuild();       // throws: source data gone

// after
// keep source splats data alive (do not dispose source meshes/data)
compound.rebuild();       // _createRetainedPartSource succeeds
Defensive patterns

Strategy: try-catch

Validate before calling

for (const proxy of proxies) {
    if (!compound._streamingStates.some(s => s.proxy === proxy) && !proxy._splatsData) {
        // retained source missing: reload the original part from source files first
        await reloadPartSource(proxy);
    }
}

Type guard

function canRebuildCompound(compound: GaussianSplattingMeshBase): boolean {
    return compound._streamingStates.every(s =>
        s.proxy._splatsData != null || s.proxy._isReservedEmpty);
}

Try / catch

try {
    compound.rebuild();
} catch (e) {
    if (e instanceof Error && e.message.includes("retained compound source data")) {
        const fresh = await reloadAllPartsFromFiles(); // re-decode original assets
        return rebuildCompoundFrom(fresh);
    }
    throw e;
}

Prevention

When it happens

Trigger: Rebuilding a compound (e.g. after context loss, `rebuild()`, or re-adding parts to a loaded compound) when a proxy part's retained source data was discarded — data retention disabled, `dispose` of sources, or memory-saving flags that skip keeping `_splatsData`.

Common situations: WebGL context restore on a compound whose part sources were freed; re-serializing or re-merging a compound loaded from a file without retained sources; enabling memory-optimization options that release source data after merge.

Related errors


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