moeru-ai/airi · error · Error
Extension entrypoint id \
Error message
Extension entrypoint id \`${extension.id}\` must match manifest id \`${options.manifest.id}\`. What it means
startExtension verifies that the id on the loaded extension entrypoint object equals the id declared in its ExtensionManifestV2. A mismatch means the entrypoint file and manifest disagree about which extension they represent, so the host aborts registration to avoid attribution bugs (contribution routing, session identity, permissions).
Solutions
- Make the extension entrypoint's exported id identical to the manifest id.
- Rebuild the extension so the entrypoint reflects the current manifest id.
- Check you are loading the correct entrypoint file for this manifest (entrypoints[runtime] or entrypoints.default).
- Clear bundler caches if a rename did not propagate to the built output.
Example fix
// before (entrypoint)
defineExtension({ id: 'old-name', setup() {} })
// after
defineExtension({ id: 'new-name', setup() {} }) Defensive patterns
Strategy: validation
Validate before calling
if (extension.id !== manifest.id) {
console.error(`entrypoint id ${extension.id} != manifest id ${manifest.id}`)
} Try / catch
try {
await host.startExtension(extension, { manifest })
} catch (e) {
if (e.message.includes('must match manifest id')) reportStaleBuild(manifest.id)
else throw e
} Prevention
- Derive the entrypoint id from the manifest/package id at build time instead of hardcoding.
- Always rebuild after renaming an extension.
- Add a unit test asserting entrypoint.id === manifest.id.
When it happens
Trigger: Calling startExtension(extension, { manifest }) where extension.id !== options.manifest.id — typically after editing a manifest's id without rebuilding the entrypoint, or loading the wrong entrypoint file for a manifest.
Common situations: Renaming an extension in package.json/manifest but shipping a stale built entrypoint still exporting the old id; copy-pasting a manifest from another extension; bundler caching an old default export.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- Extension \` \` does not support runtime \` \`.
- Extension entrypoint escapes the package folder
- Extension entrypoint is required for runtime \
- Extension manifest does not exist
- Extension \` \` does not support runtime \` \`.
AI-assisted analysis of moeru-ai/airi@438a067dde (2026-09-17).
Data as JSON: /api/errors/0760b114dc1d88c8.
Report an issue: GitHub.
Appendix: source
Thrown at packages/plugin-sdk/src/plugin-host/core.ts:261
if (!manifest.engines.runtimes.includes(runtime)) {
throw new Error(`Extension \`${manifest.id}\` does not support runtime \`${runtime}\`.`)
}
if (this.airiVersion && !semver.satisfies(this.airiVersion, manifest.engines.airi, { includePrerelease: true })) {
throw new Error(
`Extension \`${manifest.id}\` requires AIRI \`${manifest.engines.airi}\`, but the running version is \`${this.airiVersion}\`.`,
)
}
}
async startExtension(
extension: Extension,
options: { manifest: ExtensionManifestV2, cwd?: string, runtime?: PluginRuntime },
) {
const runtime = options.runtime ?? this.runtime
this.assertManifestCompatibility(options.manifest, runtime)
if (extension.id !== options.manifest.id) {
throw new Error(`Extension entrypoint id \`${extension.id}\` must match manifest id \`${options.manifest.id}\`.`)
}
const sessionIdentity = this.extensionSessionService.nextSessionIdentity()
const extensionIdentity = {
id: extension.id,
version: options.manifest.version,
sessionId: sessionIdentity.sessionId,
}
const persistedGrant = this.persistedPermissionGrants.get(extension.id)
const resolvedGrant = await this.permissionResolver?.({
identity: extensionIdentity,
manifest: options.manifest,
requested: options.manifest.permissions,
persisted: persistedGrant,
}) ?? options.manifest.permissions
const permissionSnapshot = this.permissions.initialize(sessionIdentity.sessionId, options.manifest.permissions, {
grant: resolvedGrant,
persisted: this.permissionResolver ? undefined : persistedGrant,View on GitHub (pinned to 438a067dde)