{"record":{"id":"3312fe718392b25a","repo":"moeru-ai/airi","slug":"extension-manifest-not-found-extensionid","errorCode":null,"errorMessage":"Extension manifest not found: ${extensionId}","messagePattern":"Extension manifest not found: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/stage-tamagotchi/src/main/services/airi/plugins/host/index.ts","lineNumber":353,"sourceCode":"      entries: extensionRegistry.listEntries(),\n      config: getConfig(),\n      loaded,\n      manifestEntryByExtensionId: extensionRegistry.getManifestEntryByExtensionId(),\n      extensionAssetService: extensionAssetSnapshotService,\n    })\n  }\n\n  const loadExtensionById = async (\n    extensionId: string,\n    loadOptions: { cacheBustKey?: string } = {},\n  ) => {\n    if (loaded.has(extensionId)) {\n      return\n    }\n\n    const entry = extensionRegistry.findManifestEntry(extensionId)\n    if (!entry) {\n      throw new Error(`Extension manifest not found: ${extensionId}`)\n    }\n\n    const manifestForLoad = createManifestForLoad(entry, loadOptions)\n    const session = await host.start(manifestForLoad, { cwd: dirname(entry.path) })\n    loaded.add(extensionId)\n    loadedSessionIds.set(extensionId, session.id)\n    log.withFields({ extensionId, sessionId: session.id }).log('extension loaded')\n  }\n\n  const stopLoadedExtensionById = async (extensionId: string) => {\n    const sessionId = loadedSessionIds.get(extensionId)\n    if (!sessionId) {\n      loaded.delete(extensionId)\n      return\n    }\n\n    await host.stop(sessionId)\n    loadedSessionIds.delete(extensionId)","sourceCodeStart":335,"sourceCodeEnd":371,"githubUrl":"https://github.com/moeru-ai/airi/blob/27111382b4a79a7e983289d6e983a06af185ed0f/apps/stage-tamagotchi/src/main/services/airi/plugins/host/index.ts#L335-L371","documentation":"Thrown by loadExtensionById when extensionRegistry.findManifestEntry(extensionId) returns nothing, meaning no discovered extension manifest matches the requested id. The registry is built during extension discovery/scanning, so a missing entry means the extension was never found on disk or not registered.","triggerScenarios":"loadExtensionById is called with an extensionId that was not returned by discovery — because the extension folder is absent, the manifest file is missing/malformed, or the id is misspelled. Also occurs if discovery ran before the extension was installed.","commonSituations":"A plugin references an extensionId that doesn't exist; the extension directory was moved or deleted; the manifest's declared id doesn't match the id requested; discovery扫描 hasn't run yet at the time of the load call; a packaging step omitted the extension.","solutions":["Confirm the extension directory exists under the expected extensions root and contains a valid manifest file.","Verify the extensionId string exactly matches the id declared in the extension's manifest.","Trigger or await extension discovery/registry rebuild before attempting to load.","Re-install or re-link the extension if it was removed during development."],"exampleFix":"// before\nawait loadExtensionById('my-extension')\n\n// after\nconst entry = extensionRegistry.findManifestEntry('my-extension')\nif (!entry) {\n  throw new Error(`Extension 'my-extension' not discovered. Check it is installed under the extensions root.`)\n}\nawait loadExtensionById('my-extension')","handlingStrategy":"validation","validationCode":"const entry = extensionRegistry.findManifestEntry(extensionId)\nif (!entry) {\n  throw new Error(`Cannot load '${extensionId}': not present in registry. Run discovery or install the extension.`)\n}\nawait loadExtensionById(extensionId)","typeGuard":"function isKnownExtension(id: string, registry: { findManifestEntry: (id: string) => unknown }): boolean {\n  return Boolean(registry.findManifestEntry(id))\n}","tryCatchPattern":null,"preventionTips":["Run/await extension discovery before any load-by-id call.","Surface available extension ids to the caller so it can validate before attempting to load.","Treat a missing manifest as a user-facing 'extension not installed' message rather than a crash."],"tags":["extensions","registry","manifest","lifecycle"],"backgroundTag":null,"analyzedSha":"27111382b4a79a7e983289d6e983a06af185ed0f","analyzedAt":"2026-08-12T18:33:34.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}