mattermost-community/focalboard · error

cannot get migration state: %w

Error message

cannot get migration state: %w

What it means

Wraparound error in SQLStore.RunUniqueIDsMigration (server/services/store/sqlstore/data_migrations.go:75). Before running the unique-IDs data migration, the store reads the UniqueIDsMigrationKey system setting to check whether the migration already completed. If GetSystemSetting itself returns an error (not merely an empty value), this error is wrapped so the caller (runMigrationSequence) knows migration state lookup failed.

Source

Thrown at server/services/store/sqlstore/data_migrations.go:75

	rows, err := s.getQueryBuilder(db).
		Select(blocksFields...).
		From(s.tablePrefix + "blocks").
		Where(fmt.Sprintf("id IN (%s)", subquery)).
		Query()
	if err != nil {
		s.logger.Error(`getBlocksWithSameID ERROR`, mlog.Err(err))
		return nil, err
	}
	defer s.CloseRows(rows)

	return s.blocksFromRows(rows)
}

func (s *SQLStore) RunUniqueIDsMigration() error {
	setting, err := s.GetSystemSetting(UniqueIDsMigrationKey)
	if err != nil {
		return fmt.Errorf("cannot get migration state: %w", err)
	}

	// If the migration is already completed, do not run it again.
	if hasAlreadyRun, _ := strconv.ParseBool(setting); hasAlreadyRun {
		return nil
	}

	s.logger.Debug("Running Unique IDs migration")

	tx, txErr := s.db.BeginTx(context.Background(), nil)
	if txErr != nil {
		return txErr
	}

	blocks, err := s.getBlocksWithSameID(tx)
	if err != nil {
		if rollbackErr := tx.Rollback(); rollbackErr != nil {
			s.logger.Error("Unique IDs transaction rollback error", mlog.Err(rollbackErr), mlog.String("methodName", "getBlocksWithSameID"))

View on GitHub (pinned to a84bbb65e3)

Solutions

  1. Check the wrapped %w cause for the exact DB error (connection, permissions, missing table).
  2. Verify the SystemSettings table exists and the DB user has SELECT rights.
  3. Check DB connectivity and retry server start; the migration runs only once so a healthy retry is safe.
  4. Fix schema initialization failures before the app starts migrations.
  5. If the setting value is empty/absent, note the store tolerates it (ParseBool error is ignored) — only read failures produce this error.

Example fix

// before
err := store.RunUniqueIDsMigration()
if err != nil {
	log.Fatal(err)
}
// after
err := store.RunUniqueIDsMigration()
if err != nil {
	if isTransientDBError(errors.Unwrap(err)) {
		log.Println("transient DB error, retrying migration", err)
		time.Sleep(time.Second)
		err = store.RunUniqueIDsMigration()
	}
	if err != nil {
		log.Fatal(err)
	}
}
Defensive patterns

Strategy: retry

Validate before calling

if err := db.Ping(); err != nil { return fmt.Errorf("db unavailable, defer migrations: %w", err) }

Type guard

func isMigrationStateError(err error) bool {
	return err != nil && strings.Contains(err.Error(), "cannot get migration state")
}

Try / catch

err := store.RunUniqueIDsMigration()
if err != nil {
	if isMigrationStateError(err) && isTransient(errors.Unwrap(err)) {
		time.Sleep(backoff)
		err = store.RunUniqueIDsMigration()
	}
	if err != nil { log.Fatal(err) }
}

Prevention

When it happens

Trigger: Calling RunUniqueIDsMigration when the system settings read fails: database connection error, corrupt/locked SystemSettings table, query timeout, or missing table due to incomplete schema initialization.

Common situations: Fresh installs where schema init failed; DB user lacking SELECT permission on the SystemSettings table; transient DB outage during server startup when runMigrationSequence executes; proxy/pool timeouts on large settings rows.

Related errors


AI-assisted analysis of mattermost-community/focalboard@a84bbb65e3 (2026-08-30). Data as JSON: /api/errors/a13ffdbe1f2cd811. Report an issue: GitHub.