{"record":{"id":"6890286f300d5e87","repo":"actualbudget/actual","slug":"out-of-sync-migrations","errorCode":"out-of-sync-migrations","errorMessage":"out-of-sync-migrations","messagePattern":"out-of-sync-migrations","errorType":"error_code","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"packages/loot-core/src/server/migrate/migrations.ts","lineNumber":158,"sourceCode":"  }\n  sqlite.runQuery(db, 'INSERT INTO __migrations__ (id) VALUES (?)', [\n    getMigrationId(name),\n  ]);\n}\n\nfunction checkDatabaseValidity(\n  appliedIds: number[],\n  available: string[],\n): void {\n  if (appliedIds.length > available.length) {\n    logger.error(\n      'Database is out of sync with migrations (index past available):',\n      {\n        appliedIds,\n        available,\n      },\n    );\n    throw new Error('out-of-sync-migrations');\n  }\n\n  for (let i = 0; i < appliedIds.length; i++) {\n    if (appliedIds[i] !== getMigrationId(available[i])) {\n      logger.error(\n        'Database is out of sync with migrations (migration id mismatch):',\n        {\n          appliedIds,\n          available,\n          missing: available.filter(\n            m => !appliedIds.includes(getMigrationId(m)),\n          ),\n        },\n      );\n      throw new Error('out-of-sync-migrations');\n    }\n  }\n}","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/migrate/migrations.ts#L140-L176","documentation":"checkDatabaseValidity compares the migrations applied to a database against the available migration list. If the applied index exceeds the number of available migrations, the database was made by a newer version of the app and the code logs the applied/available ids then throws this coded error to abort migrate() and prevent running on an incompatible schema.","triggerScenarios":"Opening a budget file that was migrated by a newer version of Actual (more migrations applied) with an older installed version — e.g. after downgrading the app or syncing a budget from a newer client.","commonSituations":"Downgrading the desktop app or self-hosted server below the version that last touched the budget; switching between stable and bleeding-edge releases; opening a budget copied from a newer install.","solutions":["Upgrade Actual (app and, if self-hosting, the sync server) to a version at or above the one that last modified the budget.","Do not attempt to open the budget with the older version; revert the downgrade instead.","If you must stay on the old version, restore a backup of the budget taken while that old version was in use."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// compare applied vs available before opening\nconst applied = await getAppliedMigrations(db);\nconst available = migrations.length;\nconst compatible = applied.length <= available;\nif (!compatible) throw new Error('budget made by newer version — upgrade Actual');","typeGuard":"function isCompatible(appliedIds, availableMigrations) {\n  return appliedIds.length <= availableMigrations.length;\n}","tryCatchPattern":"try {\n  await migrate(db);\n} catch (e) {\n  if (e.message === 'out-of-sync-migrations') {\n    // upgrade the app/server; do not touch the budget file\n  } else throw e;\n}","preventionTips":["Never downgrade Actual below the version that last opened a budget.","When self-hosting, upgrade the sync server before or together with clients.","Keep database backups before version changes.","Test risky upgrades on a copy of the budget first."],"tags":["database","migrations","version-mismatch"],"backgroundTag":"schema-version-mismatch","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}