mattermost-community/focalboard · error
cannot mark migration as completed: %w
Error message
cannot mark migration as completed: %w
What it means
Error from SQLStore.RunUniqueIDsMigration (server/services/store/data_migrations.go:124). After replacing duplicate IDs, the migration marks completion by writing UniqueIDsMigrationKey=true via setSystemSetting(tx, ...). If that write fails, the transaction is rolled back (all ID replacements undone) and the error is wrapped as 'cannot mark migration as completed'.
Source
Thrown at server/services/store/sqlstore/data_migrations.go:124
// do nothing for the first ID, only updating the others
continue
}
newID := utils.NewID(model.BlockType2IDType(block.Type))
if err := s.replaceBlockID(tx, block.ID, newID, block.WorkspaceID); err != nil {
if rollbackErr := tx.Rollback(); rollbackErr != nil {
s.logger.Error("Unique IDs transaction rollback error", mlog.Err(rollbackErr), mlog.String("methodName", "replaceBlockID"))
}
return fmt.Errorf("cannot replace blockID %s: %w", block.ID, err)
}
}
}
if err := s.setSystemSetting(tx, UniqueIDsMigrationKey, strconv.FormatBool(true)); err != nil {
if rollbackErr := tx.Rollback(); rollbackErr != nil {
s.logger.Error("Unique IDs transaction rollback error", mlog.Err(rollbackErr), mlog.String("methodName", "setSystemSetting"))
}
return fmt.Errorf("cannot mark migration as completed: %w", err)
}
if err := tx.Commit(); err != nil {
return fmt.Errorf("cannot commit unique IDs transaction: %w", err)
}
s.logger.Debug("Unique IDs migration finished successfully")
return nil
}
// RunCategoryUUIDIDMigration takes care of deriving the categories
// from the boards and its memberships. The name references UUID
// because of the preexisting purpose of this migration, and has been
// preserved for compatibility with already migrated instances.
func (s *SQLStore) RunCategoryUUIDIDMigration() error {
setting, err := s.GetSystemSetting(CategoryUUIDIDMigrationKey)
if err != nil {
return fmt.Errorf("cannot get migration state: %w", err)View on GitHub (pinned to a84bbb65e3)
Solutions
- Check the wrapped cause; if it is a connection timeout, increase idle/transaction timeouts and re-run.
- Ensure the DB user can INSERT/UPDATE the SystemSettings table.
- Run the migration during a maintenance window so the transaction finishes quickly; the rollback makes retry safe (idempotent flag).
- Avoid PgBouncer transaction-pooling misconfigurations that kill long transactions; use a dedicated session pool for migrations.
- If 'Unique IDs transaction rollback error' also appears in logs, verify no manual cleanup is needed before retrying.
Example fix
// before SET GLOBAL wait_timeout = 28800; -- default, too short for long txns // after SET GLOBAL wait_timeout = 86400; SET GLOBAL max_allowed_packet = 64M; -- then re-run: store.RunUniqueIDsMigration()
Defensive patterns
Strategy: retry
Validate before calling
var canWrite bool
if err := db.QueryRow("SELECT has_table_privilege('system_settings','INSERT','current_user')").Scan(&canWrite); err != nil || !canWrite { /* grant write before migrating */ } Type guard
func isMigrationFinalizeError(err error) bool {
return err != nil && strings.Contains(err.Error(), "cannot mark migration as completed")
} Try / catch
err := store.RunUniqueIDsMigration()
if err != nil {
if isMigrationFinalizeError(err) {
// store already rolled back; fix timeouts/permissions and retry safely
log.Println("migration rolled back, retryable:", errors.Unwrap(err))
}
return err
} Prevention
- Raise idle-transaction/timeout settings before long migrations
- Use a direct DB connection (not transaction-pooled) for migrations
- Grant the app user write access to SystemSettings
- Run migrations single-instance to avoid settings-row contention
When it happens
Trigger: Running RunUniqueIDsMigration when the final setSystemSetting upsert into SystemSettings fails: connection loss, deadlock on the settings row, permissions failure, or settings table schema issues — after the (possibly lengthy) ID replacement work has already succeeded inside the transaction.
Common situations: Long-running migration holding the transaction open until the connection is dropped by a proxy/idle timeout (MySQL wait_timeout, PgBouncer) right before the final write; DB user lacking UPDATE rights on SystemSettings; concurrent migration runs contending on the settings row.
Related errors
- cannot get blocks with same ID: %w
- cannot replace blockID %s: %w
- cannot save member %s while inserting board %s: %w
- cannot get migration state: %w
- cannot commit unique IDs transaction: %w
AI-assisted analysis of mattermost-community/focalboard@a84bbb65e3 (2026-08-30).
Data as JSON: /api/errors/048c8f9276ef3d17.
Report an issue: GitHub.