{"record":{"id":"3240c7e77da28c57","repo":"moeru-ai/airi","slug":"camera-is-not-perspective-camera-type-error","errorCode":null,"errorMessage":"Camera is not perspective camera, type error!","messagePattern":"Camera is not perspective camera, type error!","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/stage-ui-three/src/components/Controls/OrbitControls.vue","lineNumber":184,"sourceCode":"      },\n    )\n  }\n\n  disposeControlsChange?.()\n  controls.value?.addEventListener('change', onChange)\n  disposeControlsChange = () => controls.value?.removeEventListener('change', onChange)\n}\n\nonMounted(async () => {\n  // wait until camera is not undefined\n  await until(() => cameraTres.value && renderer.domElement).toBeTruthy()\n  if (!cameraTres.value || !renderer.domElement) {\n    console.warn('Camera or Renderer initialisation failure!')\n    return\n  }\n  // Narrow down the camera's type\n  if (!(cameraTres.value instanceof PerspectiveCamera)) {\n    console.warn('Camera is not perspective camera, type error!')\n    return\n  }\n  camera.value = cameraTres.value as PerspectiveCamera\n  // Obtain orbitControl instance\n  controls.value = new OrbitControls(camera.value, renderer.domElement)\n  controls.value.enablePan = false\n  controls.value.enableZoom = false\n  controls.value.enableRotate = false\n  // Align to tresjs conventions\n  controls.value.mouseButtons = {\n    LEFT: MOUSE.ROTATE,\n    MIDDLE: MOUSE.DOLLY,\n    RIGHT: MOUSE.PAN,\n  }\n  controls.value.touches = {\n    ONE: TOUCH.ROTATE,\n    TWO: TOUCH.DOLLY_PAN,\n  }","sourceCodeStart":166,"sourceCodeEnd":202,"githubUrl":"https://github.com/moeru-ai/airi/blob/677329427f32468c74b17f3ec47eeca4e05bec65/packages/stage-ui-three/src/components/Controls/OrbitControls.vue#L166-L202","documentation":"After awaiting the camera and renderer DOM element, the component requires cameraTres.value instanceof PerspectiveCamera. With any other camera type (e.g. an OrthographicCamera scene) it warns and returns, so controls.value is never created and the component silently does nothing. Pan/zoom/rotate are configured disabled anyway, but no OrbitControls instance exists at all after this early return.","triggerScenarios":"Scene camera is an OrthographicCamera (2D-style scene); the camera ref resolving to a non-camera object; dropping this component into a scene not built for perspective cameras.","commonSituations":"Reusing the OrbitControls component in an orthographic/2D scene; a TresJS scene where the camera slot is filled by a custom camera.","solutions":["Ensure the scene renders with a PerspectiveCamera before this component mounts","Extend the guard to support OrthographicCamera if your scene needs it","Verify cameraTres resolves to the camera object, not a wrapper or group","Null-check controls.value downstream — it stays undefined after the early return"],"exampleFix":"// before\nif (!(cameraTres.value instanceof PerspectiveCamera)) {\n  console.warn('Camera is not perspective camera, type error!')\n  return\n}\ncamera.value = cameraTres.value as PerspectiveCamera\n\n// after: reuse the narrowed guard\nif (!isPerspectiveCamera(cameraTres.value)) {\n  console.warn('Camera is not perspective camera, type error!')\n  return\n}\ncamera.value = cameraTres.value // already narrowed, no cast needed","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"import { PerspectiveCamera } from 'three'\n\nfunction isPerspectiveCamera(camera: unknown): camera is PerspectiveCamera {\n  return camera instanceof PerspectiveCamera\n}","tryCatchPattern":null,"preventionTips":["Render the scene with a PerspectiveCamera before mounting OrbitControls","If orthographic support is needed, extend the component rather than removing the guard","Verify the camera ref resolves to the camera itself, not a wrapper or group","Null-check controls.value downstream; it stays undefined after the early return"],"tags":["three","orbit-controls","perspective-camera","type-narrowing"],"backgroundTag":"threejs-camera-type-mismatch","analyzedSha":"677329427f32468c74b17f3ec47eeca4e05bec65","analyzedAt":"2026-08-18T17:29:58.153Z","contentChangedAt":"2026-08-18T17:29:58.153Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}