{"record":{"id":"23780316c849340d","repo":"BabylonJS/Babylon.js","slug":"method-this-streaming-part-has-been-removed","errorCode":null,"errorMessage":"${method}: this streaming part has been removed","messagePattern":"(.+?): this streaming part has been removed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/dev/core/src/Meshes/GaussianSplatting/gaussianSplattingMesh.pure.ts","lineNumber":2187,"sourceCode":"        };\r\n        this._streamingStates.push(state);\r\n        capacity = alignedCapacity;\r\n\r\n        // The handle's live getters need a stable reference to this compound (its atlas textures can be\r\n        // recreated on a later rebuild), so alias it for the closures below.\r\n        // eslint-disable-next-line @typescript-eslint/no-this-alias\r\n        const compound = this;\r\n        const applyBounds = () => {\r\n            if (boundsMin.x <= boundsMax.x) {\r\n                proxy.setBoundingInfo(new BoundingInfo(boundsMin.clone(), boundsMax.clone()));\r\n                compound._updateBoundingInfoFromProxies();\r\n            }\r\n        };\r\n        // Rejects any mutating call after the part was removed, so a retained stale handle can't touch a surviving\r\n        // part that inherited this region's (now-reused) part index/base.\r\n        const assertLive = (method: string) => {\r\n            if (state.removed) {\r\n                throw new Error(`${method}: this streaming part has been removed`);\r\n            }\r\n        };\r\n        // Enforces the documented local part boundary [0, capacity) so a handle call can never address another\r\n        // part's atlas region (the region base is added to these local coordinates before use).\r\n        const assertLocalRange = (method: string, offset: number, count: number) => {\r\n            if (!Number.isInteger(offset) || !Number.isInteger(count) || offset < 0 || count < 0 || offset + count > state.capacity) {\r\n                throw new Error(`${method}: local range [${offset}, ${offset + count}) is outside the reserved region [0, ${state.capacity})`);\r\n            }\r\n        };\r\n\r\n        const handle: IGaussianSplattingStreamingPart = {\r\n            proxy,\r\n            capacity,\r\n            get partIndex() {\r\n                return state.partIndex;\r\n            },\r\n            get base() {\r\n                return state.base;\r","sourceCodeStart":2169,"sourceCodeEnd":2205,"githubUrl":"https://github.com/BabylonJS/Babylon.js/blob/0592b347b8a4ee0236089ea86a749cacfdb266d8/packages/dev/core/src/Meshes/GaussianSplatting/gaussianSplattingMesh.pure.ts#L2169-L2205","documentation":"Streaming part handles returned by reserveStreamingPart assert liveness before any mutating call. Once a part is removed, its part index/base may be reused by a surviving part, so a stale retained handle is rejected to prevent it from writing into another part's atlas region.","triggerScenarios":"Keeping an IGaussianSplattingStreamingPart handle after calling its remove/dispose method (or removal via the compound mesh) and then invoking any of its write/update methods.","commonSituations":"Caching handles in app code or a streaming manager without clearing them on removal; event-driven pipelines where a removal callback races a pending write.","solutions":["Discard or null the handle immediately after removing the part","Check a liveness flag of your own before using cached handles","Re-reserve a new part (and obtain a fresh handle) instead of reusing removed ones"],"exampleFix":"// before\npart.remove();\npart.writeSplats(0, data); // throws\n// after\npart.remove();\npart = null; // or request a new part handle before writing","handlingStrategy":"type-guard","validationCode":"if (isPartRemoved(handle)) return; // skip write\nfunction isPartRemoved(h: IGaussianSplattingStreamingPart) { return (h as any)?.removed === true; }","typeGuard":"function isLive(h: IGaussianSplattingStreamingPart | null | undefined): h is IGaussianSplattingStreamingPart {\n  return !!h && !(h as any).removed;\n}","tryCatchPattern":"try {\n  handle.writeSplats(offset, data);\n} catch (e) {\n  if (String(e).includes('has been removed')) { handle = null; reReserveAndWrite(); }\n  else throw e;\n}","preventionTips":["Null out handles in the same code path that removes parts","Wrap handles in a manager that tracks removal state centrally","Avoid retaining streaming handles in long-lived collections"],"tags":["gaussian-splatting","lifecycle","stale-reference"],"backgroundTag":"use-after-dispose","analyzedSha":"0592b347b8a4ee0236089ea86a749cacfdb266d8","analyzedAt":"2026-08-30T15:11:20.442Z","schemaVersion":2},"datasetVersion":"2026-08-30T18:17:15.746Z"}