{"record":{"id":"5d63c5f20a62b5db","repo":"moeru-ai/airi","slug":"failed-to-resolve-extension-module-the-entrypoint","errorCode":null,"errorMessage":"Failed to resolve extension module. The entrypoint must export defineExtension(...).","messagePattern":"Failed to resolve extension module\\. The entrypoint must export defineExtension\\(\\.\\.\\.\\)\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/plugin-sdk/src/plugin-host/runtimes/node/loaders/fs.ts","lineNumber":28,"sourceCode":"    && 'id' in value\n    && typeof (value as { id?: unknown }).id === 'string'\n    && 'setup' in value\n    && typeof (value as { setup?: unknown }).setup === 'function'\n}\n\nfunction coerceExtensionFromModule(moduleValue: unknown): Extension {\n  if (isExtensionDefinition(moduleValue)) {\n    return moduleValue\n  }\n\n  if (typeof moduleValue === 'object' && moduleValue !== null) {\n    const defaultExport = (moduleValue as { default?: unknown }).default\n    if (isExtensionDefinition(defaultExport)) {\n      return defaultExport\n    }\n  }\n\n  throw new Error('Failed to resolve extension module. The entrypoint must export defineExtension(...).')\n}\n\n/**\n * Loads extension entrypoints from the local filesystem for the current runtime.\n *\n * Use when:\n * - The host needs to resolve a manifest entrypoint path\n * - The host needs to import a `defineExtension(...)` export\n *\n * Expects:\n * - Entry points are valid importable module paths for the active runtime\n *\n * Returns:\n * - Filesystem-backed helpers for resolving and loading extension entrypoints\n */\nexport class FileSystemLoader {\n  /**\n   * Resolve a manifest entrypoint for the requested runtime.","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/moeru-ai/airi/blob/27111382b4a79a7e983289d6e983a06af185ed0f/packages/plugin-sdk/src/plugin-host/runtimes/node/loaders/fs.ts#L10-L46","documentation":"Thrown by coerceExtensionFromModule() when the imported entrypoint module is neither itself a valid Extension (object with string `id` and function `setup`) nor has a `default` export that is. The loader expects a defineExtension(...) result either as the namespace or as the default export.","triggerScenarios":"The entrypoint file resolved by resolveEntrypointFor imports successfully but does not export defineExtension(...) output correctly — e.g. it exports a plain object, a function, or forgets `export default`. Also triggered if defineExtension is called but the result is not exported (just invoked for side effects).","commonSituations":"Entrypoint file is empty or a stub. Author wrote `defineExtension({ id, setup })` without `export default`. The file exports the extension under a named export (e.g. `export const ext = ...`) instead of default. The resolved path points at the wrong file (e.g. an index that re-exports utilities).","solutions":["Ensure the entrypoint's default export is the result of defineExtension({ id, setup }): `export default defineExtension({ id, setup })`.","Confirm the entrypoint file resolved by resolveEntrypointFor is the intended extension file, not a barrel/utility module.","Verify the entrypoint exports an object with a string `id` and a function `setup` (the isExtensionDefinition contract)."],"exampleFix":"// before — extension.ts\nimport { defineExtension } from '@proj-airi/plugin-sdk'\nconst ext = defineExtension({ id: 'my-ext', setup(ctx) { /* ... */ } }) // not exported\n\n// after\nexport default defineExtension({ id: 'my-ext', setup(ctx) { /* ... */ } })","handlingStrategy":"type-guard","validationCode":"const mod = await import(entrypoint)\nconst candidate = mod.default ?? mod\nif (typeof candidate !== 'object' || candidate === null\n    || typeof candidate.id !== 'string'\n    || typeof candidate.setup !== 'function') {\n  throw new Error(`Entrypoint '${entrypoint}' does not export a defineExtension(...) result`)\n}","typeGuard":"function isExtensionDefinition(value: unknown): value is Extension {\n  return typeof value === 'object'\n    && value !== null\n    && 'id' in value\n    && typeof (value as { id?: unknown }).id === 'string'\n    && 'setup' in value\n    && typeof (value as { setup?: unknown }).setup === 'function'\n}","tryCatchPattern":"try {\n  const extension = await loader.loadExtensionFor(manifest, options)\n} catch (error) {\n  if (error instanceof Error && /Failed to resolve extension module/.test(error.message)) {\n    // entrypoint does not export defineExtension(...); fix the entrypoint exports\n  } else {\n    throw error\n  }\n}","preventionTips":["Always `export default defineExtension({ id, setup })` from the entrypoint file.","Do not rely on named exports; the loader checks the namespace and its `default` only.","Add a smoke test that imports the entrypoint and asserts the default export has string id + function setup."],"tags":["loader","entrypoint","extension","module-resolution","defineextension"],"backgroundTag":null,"analyzedSha":"27111382b4a79a7e983289d6e983a06af185ed0f","analyzedAt":"2026-08-12T18:33:34.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}