{"record":{"id":"95bcc92fc3f96778","repo":"BabylonJS/Babylon.js","slug":"restoring-persistent-anchors-requires-the-anchor-s","errorCode":null,"errorMessage":"Restoring persistent anchors requires the anchor system to be attached","messagePattern":"Restoring persistent anchors requires the anchor system to be attached","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/dev/core/src/XR/features/WebXRAnchorSystem.pure.ts","lineNumber":331,"sourceCode":"    public async requestPersistentHandleAsync(anchor: IWebXRAnchor): Promise<string> {\r\n        const requestPersistentHandle = anchor.xrAnchor.requestPersistentHandle;\r\n        if (!requestPersistentHandle) {\r\n            throw new Error(\"Requesting persistent anchor handles is not supported in this environment/browser\");\r\n        }\r\n        const handle = await requestPersistentHandle.call(anchor.xrAnchor);\r\n        this._setPersistentHandle(anchor, handle);\r\n        return handle;\r\n    }\r\n\r\n    /**\r\n     * Restore a persistent anchor into the Babylon anchor lifecycle\r\n     * @param handle The persistent anchor handle to restore\r\n     * @returns A promise that resolves after the restored anchor is tracked by an XR frame\r\n     * @throws If restoring persistent anchors is not supported by the current session\r\n     */\r\n    public async restorePersistentAnchorAsync(handle: string): Promise<IWebXRAnchor> {\r\n        if (!this.attached) {\r\n            throw new Error(\"Restoring persistent anchors requires the anchor system to be attached\");\r\n        }\r\n\r\n        const session = this._xrSessionManager.session;\r\n        const restorePersistentAnchor = session?.restorePersistentAnchor;\r\n        if (!restorePersistentAnchor) {\r\n            throw new Error(\"Restoring persistent anchors is not supported in this environment/browser\");\r\n        }\r\n\r\n        const nativeAnchor = await restorePersistentAnchor.call(session, handle);\r\n        if (!this.attached || this._xrSessionManager.session !== session) {\r\n            nativeAnchor.delete();\r\n            throw new Error(\"Persistent anchor restoration was interrupted before tracking began\");\r\n        }\r\n        const existingAnchorIndex = this._findIndexInAnchorArray(nativeAnchor);\r\n        if (existingAnchorIndex !== -1) {\r\n            const existingAnchor = this._trackedAnchors[existingAnchorIndex];\r\n            existingAnchor.persistentHandle = handle;\r\n            return existingAnchor;\r","sourceCodeStart":313,"sourceCodeEnd":349,"githubUrl":"https://github.com/BabylonJS/Babylon.js/blob/0592b347b8a4ee0236089ea86a749cacfdb266d8/packages/dev/core/src/XR/features/WebXRAnchorSystem.pure.ts#L313-L349","documentation":"restorePersistentAnchorAsync resolves a persisted handle back into a tracked anchor, which requires an active, attached WebXR feature. If the anchor system is not currently attached to a session, the method cannot interact with the XRSession and throws this error before attempting the native restore. It is a state error, not a capability error.","triggerScenarios":"Calling restorePersistentAnchorAsync(handle) (directly or through restorePersistentAnchorsAsync) on a WebXRAnchorSystem instance that has not been attached — e.g. before the XR session starts or after the feature detached/session ended.","commonSituations":"Restoring anchors during app startup before entering XR, session ended asynchronously while restore logic was still running, forgetting to add the anchor system feature to the experience helpers.","solutions":["Only call restore after the session is running and the anchor system is attached (e.g. after onXRSessionStarted)","Check anchorSystem.attached before invoking","Ensure the feature is enabled via the WebXR experience helper so attach happens automatically","Wrap in try/catch and defer restoration until attach completes"],"exampleFix":"// before\nawait anchorSystem.restorePersistentAnchorAsync(handle);\n// after\nif (anchorSystem.attached) {\n  await anchorSystem.restorePersistentAnchorAsync(handle);\n} else {\n  xrSessionManager.onXRSessionStarted.addOnce(() =>\n    anchorSystem.restorePersistentAnchorAsync(handle)\n  );\n}","handlingStrategy":"validation","validationCode":"if (!anchorSystem.attached) {\n  throw new Error('attach the anchor system before restoring persistent anchors');\n}","typeGuard":"function isAttached(s: WebXRAnchorSystem): boolean {\n  return s.attached;\n}","tryCatchPattern":"try {\n  await anchorSystem.restorePersistentAnchorAsync(handle);\n} catch (e) {\n  if (/attached/.test((e as Error).message)) {\n    xrSessionManager.onXRSessionStarted.addOnce(() => anchorSystem.restorePersistentAnchorAsync(handle));\n  }\n}","preventionTips":["Restore anchors only after onXRSessionStarted fires","Check .attached before any restore call","Register the anchor feature with the experience helper so attach is automatic","Cancel/defer restore flows during XR teardown"],"tags":["webxr","anchors","lifecycle","state"],"backgroundTag":"xr-feature-not-attached","analyzedSha":"0592b347b8a4ee0236089ea86a749cacfdb266d8","analyzedAt":"2026-08-30T15:11:20.442Z","schemaVersion":2},"datasetVersion":"2026-08-30T18:17:15.746Z"}