{"record":{"id":"28c9257622ac42e2","repo":"BabylonJS/Babylon.js","slug":"the-xr-view-index-must-be-a-non-negative-integer","errorCode":null,"errorMessage":"The XR view index must be a non-negative integer.","messagePattern":"The XR view index must be a non-negative integer\\.","errorType":"validation","errorClass":"RangeError","httpStatus":null,"severity":"error","filePath":"packages/dev/core/src/XR/webXRSessionManager.ts","lineNumber":289,"sourceCode":"        const view = this._getCurrentXRView(viewIndex);\r\n        if (!(\"recommendedViewportScale\" in view) || typeof view.requestViewportScale !== \"function\") {\r\n            throw new Error(`Dynamic viewport scaling is not supported for XR view ${viewIndex}.`);\r\n        }\r\n        view.requestViewportScale(scale);\r\n    }\r\n\r\n    private _getCurrentXRView(viewIndex: number): XRView {\r\n        if (!this.inXRSession || !this.session) {\r\n            throw new Error(\"Dynamic viewport scaling requires an active XR session.\");\r\n        }\r\n        if (!this.inXRFrameLoop || !this.currentFrame) {\r\n            throw new Error(\"Dynamic viewport scaling must be used during an active XR frame.\");\r\n        }\r\n        if (!this._referenceSpaceInitialized) {\r\n            throw new Error(\"Dynamic viewport scaling requires an initialized XR reference space.\");\r\n        }\r\n        if (!Number.isInteger(viewIndex) || viewIndex < 0) {\r\n            throw new RangeError(\"The XR view index must be a non-negative integer.\");\r\n        }\r\n\r\n        const pose = this.currentFrame.getViewerPose(this.referenceSpace);\r\n        if (!pose || viewIndex >= pose.views.length) {\r\n            throw new RangeError(`XR view ${viewIndex} is not available in the current viewer pose.`);\r\n        }\r\n        return pose.views[viewIndex];\r\n    }\r\n\r\n    /**\r\n     * Obtains the XR graphics binding for the current session, creating it lazily.\r\n     * This is the API-agnostic seam used by WebGL and WebGPU XR features to share a binding.\r\n     * @returns the XR graphics binding for the current session\r\n     * @internal\r\n     */\r\n    public _getGraphicsBinding(): WebXRGraphicsBinding {\r\n        if (!this._engine) {\r\n            throw new Error(\"Cannot create the XR graphics binding: the engine has been disposed.\");\r","sourceCodeStart":271,"sourceCodeEnd":307,"githubUrl":"https://github.com/BabylonJS/Babylon.js/blob/0592b347b8a4ee0236089ea86a749cacfdb266d8/packages/dev/core/src/XR/webXRSessionManager.ts#L271-L307","documentation":"The viewIndex argument passed to _getCurrentXRView (via view()) must be a whole number >= 0 because it indexes into pose.views, a JavaScript array of XRViews. Non-integer or negative values can never map to a view, so a RangeError is thrown up front before any WebXR API is touched.","triggerScenarios":"Calling view(-1), view(0.5), view(NaN) or view(undefined coerced) — typically from a loop with a wrong bound, from Math.random-derived values, or by passing an eye identifier string/enum instead of an integer index.","commonSituations":"Iterating views with a wrong increment; confusing eye enums (LEFT/RIGHT) with numeric indices; a variable that was expected to be an eye index but holds a float from a division or a NaN from a failed parse.","solutions":["Pass a non-negative integer index (0, 1, ...) matching the eye/view you want.","Sanitize the value first: use Math.floor for derived floats and validate `Number.isInteger(i) && i >= 0` before the call.","If you have an eye constant, map it to the correct integer index before calling view().","Loop over views using pose.views.length bounds rather than a hard-coded upper limit."],"exampleFix":"// before\nconst idx = eyes.length / 2; // 1.5 -> throws\nconst v = xr.view(idx);\n// after\nconst idx = Math.floor(eyes.length / 2);\nif (Number.isInteger(idx) && idx >= 0) {\n  const v = xr.view(idx);\n}","handlingStrategy":"validation","validationCode":"const viewIndex: unknown = getUserIndex();\nif (typeof viewIndex !== 'number' || !Number.isInteger(viewIndex) || viewIndex < 0) {\n  throw new RangeError('view index must be a non-negative integer');\n}\nconst v = xr.view(viewIndex);","typeGuard":"function isValidXRViewIndex(i: unknown): i is number {\n  return typeof i === 'number' && Number.isInteger(i) && i >= 0;\n}","tryCatchPattern":"try {\n  const v = xr.view(idx);\n} catch (e) {\n  if (e instanceof RangeError && e.message.includes('non-negative integer')) {\n    console.error(`Bad view index: ${idx}`);\n    return;\n  }\n  throw e;\n}","preventionTips":["Type view-index variables as number and validate at boundaries","Map eye constants/enums to integer indices before calling view()","Use Math.floor/|0 on computed indices","Avoid NaN-producing expressions feeding the index (guard divisions/parses)"],"tags":["webxr","argument-validation","range-error"],"backgroundTag":"invalid-index-argument","analyzedSha":"0592b347b8a4ee0236089ea86a749cacfdb266d8","analyzedAt":"2026-08-30T15:11:20.442Z","schemaVersion":2},"datasetVersion":"2026-08-30T18:17:15.746Z"}