{"record":{"id":"7f563ec9dcc40e6a","repo":"moeru-ai/airi","slug":"mediapipe-backend-not-initialized-call-init-fir","errorCode":null,"errorMessage":"MediaPipe backend not initialized (call init() first)","messagePattern":"MediaPipe backend not initialized \\(call init\\(\\) first\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/model-driver-mediapipe/src/backends/mediapipe.ts","lineNumber":90,"sourceCode":"  }\n\n  async function ensureFaceLandmarker() {\n    if (faceLandmarker)\n      return faceLandmarker\n\n    const { FaceLandmarker } = tasksVision!\n    faceLandmarker = await FaceLandmarker.createFromOptions(vision!, {\n      baseOptions: { modelAssetPath: visionTaskAssets.face },\n      runningMode: 'VIDEO',\n      numFaces: 1,\n    })\n\n    return faceLandmarker\n  }\n\n  async function run(frame: TexImageSource, jobs: MocapJob[], nowMs: number): Promise<PerceptionPartial> {\n    if (!config)\n      throw new Error('MediaPipe backend not initialized (call init() first)')\n\n    await semaphore.acquire()\n    busy = true\n    try {\n      const partial: PerceptionPartial = {}\n\n      for (const job of jobs) {\n        if (!config.enabled[job])\n          continue\n\n        if (job === 'pose') {\n          const landmarker = await ensurePoseLandmarker()\n          const res: PoseLandmarkerResult = landmarker.detectForVideo(frame, nowMs)\n          const firstPose: NormalizedLandmark[] = res.landmarks[0] ?? []\n          const firstWorld: Landmark[] = res.worldLandmarks[0] ?? []\n          partial.pose = {\n            landmarks2d: firstPose,\n            worldLandmarks: firstWorld.map(p => ({","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/moeru-ai/airi/blob/677329427f32468c74b17f3ec47eeca4e05bec65/packages/model-driver-mediapipe/src/backends/mediapipe.ts#L72-L108","documentation":"createMediaPipeBackend() in packages/model-driver-mediapipe keeps module-closed state (config, tasksVision, vision FilesetResolver) that is only populated by init(nextConfig). run(frame, jobs, nowMs) throws this error when config is still undefined, i.e. the perception loop started calling run() before anyone awaited init(). The check is the backend's fail-fast contract: landmarks, the WASM fileset, and per-job enablement flags all depend on init having run.","triggerScenarios":"Calling backend.run() (directly or via createMocapEngine's scheduler) without awaiting createMediaPipeBackend().init(config) first; init() still in flight (dynamic import of @mediapipe/tasks-vision + FilesetResolver.forVisionTasks) when the first animation frame triggers run; a code path that constructs a fresh backend per component mount but only inits it on a branch that did not execute.","commonSituations":"A Vue composable starts the rAF loop in onMounted but awaits init in a later tick or a conditional; hot-reload during development re-creates the backend without re-running init; engine wired up in devtools before the async init promise resolves.","solutions":["Await init(config) before starting the engine loop: const backend = createMediaPipeBackend(); await backend.init(config); engine.start(backend).","If init is async and unavoidable in parallel with the loop, guard run calls or buffer frames until a ready flag flips (the engine already drops frames when isBusy() is true — also respect readiness).","On component teardown/re-mount, re-init the new backend instance because the old instance's config does not transfer."],"exampleFix":"// before\nconst backend = createMediaPipeBackend()\nengine.start(backend) // first run() throws: init never awaited\nawait backend.init(config)\n\n// after\nconst backend = createMediaPipeBackend()\nawait backend.init(config)\nengine.start(backend)","handlingStrategy":"validation","validationCode":"const backend = createMediaPipeBackend()\nconst ready = backend.init(config) // promise\nawait ready // block the loop start on successful init\nengine.start(backend)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat init() as part of construction: never hand a backend to the scheduler without awaiting init.","If init is lazy by design, expose a ready promise and have the engine drop frames until it resolves (mirroring the isBusy() drop policy).","On component unmount/remount, create and init a fresh backend — closed-over config does not survive re-creation."],"tags":["mediapipe","mocap","lifecycle","initialization","async"],"backgroundTag":"backend-not-initialized","analyzedSha":"677329427f32468c74b17f3ec47eeca4e05bec65","analyzedAt":"2026-08-18T17:29:58.153Z","contentChangedAt":"2026-08-18T17:29:58.153Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}