{"record":{"id":"6b1779f3049640a6","repo":"moeru-ai/airi","slug":"extension-copy-does-not-match-the-reviewed-package","errorCode":null,"errorMessage":"Extension copy does not match the reviewed package.","messagePattern":"Extension copy does not match the reviewed package\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/stage-tamagotchi/src/main/services/airi/plugins/host/directory-import.ts","lineNumber":499,"sourceCode":"    }\n    return await this.withSecurityScopedAccess(storedPlan.securityScopedBookmark, async () => {\n      const inspected = await inspectExtensionDirectory(storedPlan.sourcePath)\n      if (inspected.manifest.id !== storedPlan.preview.extensionId || inspected.fingerprint !== storedPlan.fingerprint) {\n        throw new Error('Extension source changed after review. Select the folder again.')\n      }\n\n      await this.assertDestinationAvailable(inspected.manifest.id)\n      await mkdir(this.extensionsRoot, { recursive: true })\n      const stagingRoot = join(this.extensionsRoot, '.imports')\n      await mkdir(stagingRoot, { recursive: true })\n      const stagingPath = join(stagingRoot, planId)\n      const destination = join(this.extensionsRoot, inspected.manifest.id)\n\n      try {\n        await copyExtensionDirectory(inspected.sourcePath, stagingPath)\n        const staged = await inspectExtensionDirectory(stagingPath)\n        if (staged.manifest.id !== inspected.manifest.id || staged.fingerprint !== inspected.fingerprint) {\n          throw new Error('Extension copy does not match the reviewed package.')\n        }\n        await this.assertDestinationAvailable(inspected.manifest.id)\n        await rename(stagingPath, destination)\n        this.plans.delete(planId)\n        return {\n          manifest: staged.manifest,\n          path: join(destination, extensionManifestFileName),\n          rootDir: destination,\n        }\n      }\n      catch (error) {\n        await rm(stagingPath, { recursive: true, force: true })\n        throw error\n      }\n    })\n  }\n}\n","sourceCodeStart":481,"sourceCodeEnd":517,"githubUrl":"https://github.com/moeru-ai/airi/blob/438a067dde47aa0bdb46c2323d1fe293dc805218/apps/stage-tamagotchi/src/main/services/airi/plugins/host/directory-import.ts#L481-L517","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Retry the import; transient mid-copy modification or I/O hiccups usually clear on a clean second attempt.","Ensure the source folder is not being modified during the copy (pause sync tools, close editors/build watchers).","Check free disk space and write permissions on extensionsRoot; clear leftover directories under <extensionsRoot>/.imports.","If it persists, copy the extension folder manually to a local stable path and import from there."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// pre-flight: ensure disk space and writability of extensionsRoot\nawait mkdir(extensionsRoot, { recursive: true })\nawait access(extensionsRoot, constants.W_OK)","typeGuard":null,"tryCatchPattern":"let lastError\nfor (let attempt = 0; attempt < 2; attempt++) {\n  try {\n    return await importAndCommit(planId)\n  } catch (error) {\n    lastError = error\n    if (!error.message.includes('does not match the reviewed package')) throw error\n  }\n}\nthrow lastError","preventionTips":["Keep the source folder quiescent during import (no editors, watchers, or sync tools writing to it).","Ensure adequate free disk space in the volume hosting extensionsRoot.","Clear leftover staging directories under extensionsRoot/.imports after failures.","Import from a local disk path rather than a network/synced volume when possible."],"tags":["filesystem","copy","integrity-check","extension-import"],"backgroundTag":"checksum-mismatch","analyzedSha":"438a067dde47aa0bdb46c2323d1fe293dc805218","analyzedAt":"2026-09-17T01:14:42.644Z","contentChangedAt":"2026-09-17T01:14:42.644Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}