moeru-ai/airi · error

Extension copy does not match the reviewed package.

Error message

Extension copy does not match the reviewed package.

What it means

After copying the extension into a staging directory under <extensionsRoot>/.imports, the service re-inspects the staged copy and compares its manifest id and fingerprint to the inspected source. A mismatch means the copy operation produced files differing from the reviewed package (corrupted or partial copy). The service aborts before renaming the staging directory to its final destination.

Solutions

  1. Retry the import; transient mid-copy modification or I/O hiccups usually clear on a clean second attempt.
  2. Ensure the source folder is not being modified during the copy (pause sync tools, close editors/build watchers).
  3. Check free disk space and write permissions on extensionsRoot; clear leftover directories under <extensionsRoot>/.imports.
  4. If it persists, copy the extension folder manually to a local stable path and import from there.
Defensive patterns

Strategy: retry

Validate before calling

// pre-flight: ensure disk space and writability of extensionsRoot
await mkdir(extensionsRoot, { recursive: true })
await access(extensionsRoot, constants.W_OK)

Try / catch

let lastError
for (let attempt = 0; attempt < 2; attempt++) {
  try {
    return await importAndCommit(planId)
  } catch (error) {
    lastError = error
    if (!error.message.includes('does not match the reviewed package')) throw error
  }
}
throw lastError

Prevention

When it happens

Trigger: copyExtensionDirectory wrote incomplete or altered files (e.g. source files changed mid-copy, filesystem error during copy that copy did not propagate, symlink/case-sensitivity differences on the target volume) causing staged.fingerprint != inspected.fingerprint or a manifest id difference.

Common situations: Source folder modified while the copy was running; disk-full or permission errors silently degrading the copy; network/home-sync volumes with flaky behavior; copy across filesystems with different symlink/case handling; antivirus interference.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


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

Appendix: source

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

    }
    return await this.withSecurityScopedAccess(storedPlan.securityScopedBookmark, async () => {
      const inspected = await inspectExtensionDirectory(storedPlan.sourcePath)
      if (inspected.manifest.id !== storedPlan.preview.extensionId || inspected.fingerprint !== storedPlan.fingerprint) {
        throw new Error('Extension source changed after review. Select the folder again.')
      }

      await this.assertDestinationAvailable(inspected.manifest.id)
      await mkdir(this.extensionsRoot, { recursive: true })
      const stagingRoot = join(this.extensionsRoot, '.imports')
      await mkdir(stagingRoot, { recursive: true })
      const stagingPath = join(stagingRoot, planId)
      const destination = join(this.extensionsRoot, inspected.manifest.id)

      try {
        await copyExtensionDirectory(inspected.sourcePath, stagingPath)
        const staged = await inspectExtensionDirectory(stagingPath)
        if (staged.manifest.id !== inspected.manifest.id || staged.fingerprint !== inspected.fingerprint) {
          throw new Error('Extension copy does not match the reviewed package.')
        }
        await this.assertDestinationAvailable(inspected.manifest.id)
        await rename(stagingPath, destination)
        this.plans.delete(planId)
        return {
          manifest: staged.manifest,
          path: join(destination, extensionManifestFileName),
          rootDir: destination,
        }
      }
      catch (error) {
        await rm(stagingPath, { recursive: true, force: true })
        throw error
      }
    })
  }
}

View on GitHub (pinned to 438a067dde)