moeru-ai/airi · error · Error

Extension manifest does not exist

Error message

Extension manifest does not exist: ${manifestPath}

What it means

After the walk, the collected file list is searched for the manifest's relative path. If the manifest file was not found among the walked entries, this error is thrown. It usually means the file disappeared between the assertRegularFile check and the end of the walk, or the walk did not reach the path where the manifest was checked.

Solutions

  1. Ensure a file named exactly per extensionManifestFileName (e.g. manifest.json, correct case) exists at the root of the selected folder, then retry.
  2. Replace a symlinked manifest with a real file copy.
  3. Stop concurrent build/edit processes modifying the folder and re-select it.
  4. Re-extract or re-copy the extension package; the copy may be incomplete.

Example fix

// before: folder has Manifest.json (wrong case on Linux)
await inspected('my-ext/') // -> manifest not found
// after
// mv my-ext/Manifest.json my-ext/manifest.json
await inspected('my-ext/')
Defensive patterns

Strategy: validation

Validate before calling

import { access, constants } from 'node:fs/promises'
import { join } from 'node:path'
await access(join(folder, 'manifest.json'), constants.R_OK) // throws if manifest missing/unreadable

Try / catch

try {
  await inspected(folder)
} catch (error) {
  if (error instanceof Error && error.message.startsWith('Extension manifest does not exist')) {
    showUserError('The selected folder has no manifest.json at its root; check name/case and re-select.')
    return
  }
  throw error
}

Prevention

When it happens

Trigger: Calling inspectExtensionDirectory when the expected manifest file (join(sourceRealPath, extensionManifestFileName)) was missing at walk time — deleted or renamed after the initial assertRegularFile, or present as a symlink/special file (filtered out by earlier checks).

Common situations: A build step renamed manifest.json mid-import; the manifest is a symlink to a shared file (rejected earlier by the symlink rule); the user edited/deleted the manifest while the import dialog was open; case-sensitivity mismatch (Manifest.json vs manifest.json on Linux).

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of moeru-ai/airi@438a067dde (2026-09-17). Data as JSON: /api/errors/dc72c1186e8e017f. Report an issue: GitHub.

Appendix: source

Thrown at apps/stage-tamagotchi/src/main/services/airi/plugins/host/directory-import.ts:207

      }
      if (!stats.isFile()) {
        throw new Error(`Extension packages can contain only files and directories: ${relativePath}`)
      }
      totalBytes += stats.size
      if (totalBytes > extensionPackageLimits.totalBytes) {
        throw new Error('Extension package exceeds the 512 MiB size limit.')
      }
      files.push({ path, relativePath, size: stats.size })
    }
  }

  directories.sort()
  files.sort((left, right) => left.relativePath.localeCompare(right.relativePath))

  const manifestRelativePath = relative(sourceRealPath, manifestPath)
  const manifestFile = files.find(file => file.relativePath === manifestRelativePath)
  if (!manifestFile) {
    throw new Error(`Extension manifest does not exist: ${manifestPath}`)
  }
  if (manifestFile.size > extensionPackageLimits.manifestBytes) {
    throw new Error('Extension manifest exceeds the 1 MiB size limit.')
  }

  // 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}`)
    }

View on GitHub (pinned to 438a067dde)