moeru-ai/airi · error · Error
Extension manifest is invalid
Error message
Extension manifest is invalid: ${formatManifestDiagnostics(parsedManifest.diagnostics)} What it means
After JSON parsing succeeds, the raw value is validated with parseExtensionManifest (a Valibot-based schema parser). If the schema check fails, the collected diagnostics are formatted into this error. The manifest is syntactically valid JSON but does not match the required extension manifest schema (missing/misspelled/incorrectly-typed fields).
Solutions
- Read the diagnostics in the message — they pinpoint each failing field — and fix the manifest to match the current extension manifest schema.
- Compare against a known-good example manifest from the docs or another working extension.
- Check for typos in field names and ensure required fields exist with correct types.
- Update the extension to the current manifest format if it targets an older schema version.
Example fix
// before: manifest.json
{ "entrypoints": { "main": "index.js" } } // entrypoint must be object, not string
// after
{ "name": "my-ext", "entrypoints": { "main": { "path": "index.js" } } } Defensive patterns
Strategy: validation
Validate before calling
import { parseExtensionManifest } from '@proj-airi/...' // whatever module owns the schema
const raw = JSON.parse(await readFile(join(folder, 'manifest.json'), 'utf8'))
const parsed = parseExtensionManifest(raw)
if (!parsed.success)
console.error(formatManifestDiagnostics(parsed.diagnostics)) // fix before importing Type guard
function isValidManifest(value: unknown): value is ExtensionManifest {
return parseExtensionManifest(value).success
} Try / catch
try {
await inspected(folder)
} catch (error) {
if (error instanceof Error && error.message.startsWith('Extension manifest is invalid')) {
showUserError(`Manifest schema errors: ${error.message}`)
return
}
throw error
} Prevention
- Run the same parseExtensionManifest schema check in your extension's CI before release.
- Keep the manifest aligned with the current host schema version; re-validate after host updates.
- Start from a known-good example manifest instead of writing one from scratch.
- Read the formatted diagnostics — they name each offending field and expected type.
When it happens
Trigger: Calling inspectExtensionDirectory when manifest.json parses as JSON but fails schema validation — missing required fields like name or entrypoints, wrong value types (number where string expected), unknown enum values, or nested entrypoint objects failing their own constraints.
Common situations: Hand-writing a manifest without the schema reference; an older extension using a previous manifest format; a field typo ("entypoints"); entrypoints entries with wrong shape; version drift after the host added stricter validation.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- audio models upstream returned malformed body
- audio voices upstream returned malformed body
- Extension entrypoint escapes the package folder
- Extension entrypoint id
- Extension entrypoint id \
AI-assisted analysis of moeru-ai/airi@438a067dde (2026-09-17).
Data as JSON: /api/errors/7eba037bcb719ca2.
Report an issue: GitHub.
Appendix: source
Thrown at apps/stage-tamagotchi/src/main/services/airi/plugins/host/directory-import.ts:231
// The manifest remains the only retained byte snapshot because the preview
// and fingerprint must describe the same manifest contents. The stable file
// handle limits the allocation even if another process grows the path.
const manifestContents = await readManifestSnapshot(manifestFile.path, manifestFile.size)
let rawManifest: unknown
try {
rawManifest = JSON.parse(manifestContents.toString('utf8')) as unknown
}
catch (error) {
if (error instanceof SyntaxError) {
throw new Error(`Extension manifest is not valid JSON: ${error.message}`)
}
throw error
}
const parsedManifest = parseExtensionManifest(rawManifest)
if (!parsedManifest.success) {
throw new Error(`Extension manifest is invalid: ${formatManifestDiagnostics(parsedManifest.diagnostics)}`)
}
for (const entrypoint of Object.values(parsedManifest.manifest.entrypoints)) {
if (!entrypoint) {
continue
}
if (isAbsolute(entrypoint)) {
throw new Error(`Imported Extension entrypoints must be relative paths: ${entrypoint}`)
}
const resolvedEntrypoint = resolve(sourceRealPath, entrypoint)
if (!isContainedPath(sourceRealPath, resolvedEntrypoint)) {
throw new Error(`Extension entrypoint escapes the package folder: ${entrypoint}`)
}
await assertRegularFile(resolvedEntrypoint, 'Extension entrypoint')
const entrypointRealPath = await realpath(resolvedEntrypoint)
if (!isContainedPath(sourceRealPath, entrypointRealPath)) {
throw new Error(`Extension entrypoint resolves outside the package folder: ${entrypoint}`)
}View on GitHub (pinned to 438a067dde)