paperclipai/paperclip · error · Error

Plugin migration checksum mismatch for ${migrationKey}

Error message

Plugin migration checksum mismatch for ${migrationKey}

What it means

Immutability guard in applyWithClient: the migration file's content hash no longer matches the checksum recorded when it was first applied. Editing applied migration files is forbidden (it makes environments diverge), so re-apply aborts; the modified migration file is at fault.

Source

Thrown at server/src/services/plugin-database.ts:492

      const migrationFiles = await listSqlMigrationFiles(migrationDir);
      const coreReadTables = manifest.database.coreReadTables ?? [];
      const lockKey = Number.parseInt(createHash("sha256").update(pluginId).digest("hex").slice(0, 12), 16);
      const persistFailure = options.persistFailure ?? true;

      const applyWithClient = async (client: PluginDatabaseClient) => {
        await client.execute(sql`SELECT pg_advisory_xact_lock(${lockKey})`);
        for (const migrationKey of migrationFiles) {
          const content = await readFile(path.join(migrationDir, migrationKey), "utf8");
          const checksum = createHash("sha256").update(content).digest("hex");
          const existingRows = await client
            .select()
            .from(pluginMigrations)
            .where(and(eq(pluginMigrations.pluginId, pluginId), eq(pluginMigrations.migrationKey, migrationKey)))
            .limit(1);
          const existing = existingRows[0] as PluginMigrationRecord | undefined;
          if (existing?.status === "applied") {
            if (existing.checksum !== checksum) {
              throw new Error(`Plugin migration checksum mismatch for ${migrationKey}`);
            }
            continue;
          }

          const statements = splitSqlStatements(content);
          try {
            if (statements.length === 0) {
              throw new Error(`Plugin migration ${migrationKey} is empty`);
            }
            for (const statement of statements) {
              validatePluginMigrationStatement(statement, namespace.namespaceName, coreReadTables);
              await client.execute(sql.raw(statement));
            }
            await client
              .insert(pluginMigrations)
              .values({
                pluginId,
                pluginKey: manifest.id,

View on GitHub (pinned to 120ae5428f)

Solutions

  1. Restore the migration file to match the recorded checksum, or repair the migration history intentionally.
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at server/src/services/plugin-database.ts:492 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of paperclipai/paperclip@120ae5428f (2026-08-18). Data as JSON: /api/errors/06923ae6daefc7c2. Report an issue: GitHub.