{"record":{"id":"1ebf540e380cdd91","repo":"moeru-ai/airi","slug":"unable-to-reload-missing-extension-session-sess","errorCode":null,"errorMessage":"Unable to reload missing extension session: ${sessionId}","messagePattern":"Unable to reload missing extension session: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/plugin-sdk/src/plugin-host/core.ts","lineNumber":870,"sourceCode":"    return await this.dependencies.waitFor(key, timeoutMs)\n  }\n\n  async stop(sessionId: string): Promise<ExtensionSession | undefined> {\n    const extensionSession = this.extensionSessionService.get(sessionId)\n    if (!extensionSession) {\n      return undefined\n    }\n\n    await this.cleanupExtensionSession(extensionSession)\n    return extensionSession\n  }\n\n  async reload(sessionId: string, options: ExtensionStartOptions = {}): Promise<ExtensionSession> {\n    // Reload preserves manifest/runtime intent, then performs stop + fresh start.\n    // This intentionally creates a new session identity for deterministic re-bootstrap.\n    const previousExtension = this.extensionSessionService.get(sessionId)\n    if (!previousExtension) {\n      throw new Error(`Unable to reload missing extension session: ${sessionId}`)\n    }\n\n    const manifest = previousExtension.manifest\n    await this.cleanupExtensionSession(previousExtension)\n    return this.start(manifest, {\n      ...options,\n      cwd: options.cwd ?? previousExtension.cwd,\n      runtime: options.runtime ?? previousExtension.runtime,\n    })\n  }\n}\n","sourceCodeStart":852,"sourceCodeEnd":882,"githubUrl":"https://github.com/moeru-ai/airi/blob/438a067dde47aa0bdb46c2323d1fe293dc805218/packages/plugin-sdk/src/plugin-host/core.ts#L852-L882","documentation":"Thrown by ExtensionHost.reload(sessionId) when extensionSessionService.get(sessionId) returns nothing. reload intentionally performs stop + fresh start and needs the previous session's manifest, cwd, and runtime to preserve intent, so it refuses to operate on an unknown id. The session may never have existed, or it was already stopped/cleaned up.","triggerScenarios":"Calling host.reload('sess-1') before host.start returned that session; reloading after host.stop(sessionId) already cleaned it up; two concurrent reloads where the first one's cleanup removes the session before the second runs; persisting a sessionId across host restarts (the in-memory session service is per host instance) and reloading it on a new host.","commonSituations":"Dev-tooling 'reload extension' buttons that keep a stale session id; retry logic that re-fires reload after the first succeeded; splitting start/stop and reload across processes or test setups where each test creates a fresh host; races between UI-triggered reload and automatic teardown.","solutions":["Check existence first: if (!host.getSession(sessionId)) { session = await host.start(manifest) } else { session = await host.reload(sessionId) }.","List live sessions via host.listSessions() to discover the current ids instead of trusting a cached one.","Guard against double reloads (e.g. an in-flight promise flag) so the second call cannot run after the first cleaned the session up.","If the host was recreated (test/dev restart), re-start the extension from its manifest rather than reloading old ids."],"exampleFix":"// before\nconst session = await host.reload(sessionId) // throws when the session is gone\n\n// after\nconst session = host.getSession(sessionId)\n  ? await host.reload(sessionId)\n  : await host.start(manifest)","handlingStrategy":"validation","validationCode":"// Before reloading:\nconst existing = host.getSession(sessionId)\nconst next = existing\n  ? await host.reload(sessionId)\n  : await host.start(manifest)","typeGuard":"const canReload = (host: ExtensionHost, sessionId: string): boolean => host.getSession(sessionId) !== undefined","tryCatchPattern":"try {\n  await host.reload(sessionId)\n} catch (error) {\n  if (error instanceof Error && error.message.startsWith('Unable to reload missing extension session')) {\n    // fall back to a fresh start from the persisted manifest\n    return host.start(manifest)\n  }\n  throw error\n}","preventionTips":["Treat sessionId as ephemeral per host instance; persist the manifest/path for recovery instead.","Serialize reload/stop operations for the same session (in-flight flag) so cleanup cannot race a second reload.","Refresh UI state from host.listSessions() rather than caching ids."],"tags":["plugin-sdk","session","reload","lifecycle"],"backgroundTag":"session-not-found","analyzedSha":"438a067dde47aa0bdb46c2323d1fe293dc805218","analyzedAt":"2026-08-18T17:29:58.153Z","contentChangedAt":"2026-08-18T17:29:58.153Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}