{"record":{"id":"e2a2b5e5dac21c9a","repo":"moeru-ai/airi","slug":"extension-asset-server-base-url-is-unavailable-st","errorCode":null,"errorMessage":"Extension asset server base URL is unavailable; start the asset server before creating asset sessions","messagePattern":"Extension asset server base URL is unavailable; start the asset server before creating asset sessions","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/stage-tamagotchi/src/main/services/airi/plugins/features/static-assets/index.ts","lineNumber":221,"sourceCode":"      await revokeSessions(server.revokeAll())\n      await server.stop()\n    },\n    getBaseUrl() {\n      return readBaseUrl()\n    },\n    async createAssetSession(input) {\n      const session = server.createSession({\n        extensionId: input.extensionId,\n        version: input.version,\n        ownerSessionId: input.ownerSessionId,\n        pathPrefix: input.pathPrefix,\n        ttlMs: input.ttlMs,\n      })\n\n      try {\n        const baseUrl = readBaseUrl()\n        if (!baseUrl) {\n          throw new Error('Extension asset server base URL is unavailable; start the asset server before creating asset sessions')\n        }\n\n        const mountedPath = buildMountedStaticAssetPath({\n          extensionId: input.extensionId,\n          assetSessionId: session.assetSessionId,\n          assetPath: input.routeAssetPath,\n        })\n\n        if (!mountedPath) {\n          throw new RangeError('Extension asset session routeAssetPath must be a safe extension asset path')\n        }\n\n        const cookie = createExtensionAssetCookie(baseUrl, session)\n        await options.cookieAdapter.setCookie(cookie)\n\n        return {\n          url: new URL(mountedPath, baseUrl).toString(),\n          assetSessionId: session.assetSessionId,","sourceCodeStart":203,"sourceCodeEnd":239,"githubUrl":"https://github.com/moeru-ai/airi/blob/27111382b4a79a7e983289d6e983a06af185ed0f/apps/stage-tamagotchi/src/main/services/airi/plugins/features/static-assets/index.ts#L203-L239","documentation":"Thrown inside createAssetSession when readBaseUrl() returns a falsy value, meaning the extension asset HTTP server has no resolvable base URL yet. Asset sessions produce URLs that embed the server's base URL, so creating a session before the server is listening would yield unusable URLs.","triggerScenarios":"An extension (or the plugins/features/static-assets module) calls createAssetSession before the underlying asset HTTP server has been started, or while it is still binding its port and readBaseUrl has not yet captured the listening address.","commonSituations":"Plugin initialization order is wrong — the static-assets feature is invoked before the HTTP server bootstrap completes; the server failed to start (port in use) so readBaseUrl never gets set; a race during app startup where an extension load triggers createAssetSession during the boot window.","solutions":["Ensure the asset HTTP server is started (and readBaseUrl is populated) before any code path calls createAssetSession.","Check the asset server startup logs for a bind/listen failure (port conflict, permission denied) and resolve it.","Defer createAssetSession calls until after the server-ready signal is emitted, or await the server start promise.","If the server URL must be configured manually, verify the configured base URL setting is non-empty."],"exampleFix":"// before\nconst session = await createAssetSession({ extensionId, ... })\n\n// after\nawait ensureAssetServerStarted()\nconst session = await createAssetSession({ extensionId, ... })","handlingStrategy":"validation","validationCode":"// Ensure the asset server base URL is available before creating a session\nawait ensureAssetServerReady()\nconst baseUrl = readBaseUrl()\nif (!baseUrl) {\n  throw new Error('Asset server not ready; cannot create session')\n}\nconst session = await createAssetSession(input)","typeGuard":null,"tryCatchPattern":"try {\n  return await createAssetSession(input)\n} catch (e) {\n  if (/base URL is unavailable/.test(errorMessageFrom(e) ?? '')) {\n    await ensureAssetServerReady()\n    return await createAssetSession(input)\n  }\n  throw e\n}","preventionTips":["Order startup so the asset HTTP server starts before features that call createAssetSession.","Expose a readiness signal/promise from the asset server and await it in dependent features.","Fail fast in the server start path (log bind errors) so readBaseUrl being empty is diagnosable."],"tags":["extensions","static-assets","lifecycle","startup-order"],"backgroundTag":null,"analyzedSha":"27111382b4a79a7e983289d6e983a06af185ed0f","analyzedAt":"2026-08-12T18:33:34.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}