mattermost-community/focalboard · error

cannot get blocks with same ID: %w

Error message

cannot get blocks with same ID: %w

What it means

Error from SQLStore.RunUniqueIDsMigration (server/services/store/sqlstore/data_migrations.go:95). Inside a transaction, the migration calls getBlocksWithSameID(tx) to find blocks whose IDs are duplicated. If that query fails, the transaction is rolled back (logging a rollback error if that also fails) and the failure is wrapped as 'cannot get blocks with same ID'.

Source

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

	// 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"))
		}
		return fmt.Errorf("cannot get blocks with same ID: %w", err)
	}

	blocksByID := map[string][]*model.Block{}
	for _, block := range blocks {
		blocksByID[block.ID] = append(blocksByID[block.ID], block)
	}

	for _, blocks := range blocksByID {
		for i, block := range blocks {
			if i == 0 {
				// 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"))

View on GitHub (pinned to a84bbb65e3)

Solutions

  1. Check the wrapped cause for the exact DB failure (timeout, connection, schema).
  2. Rerun after resolving transient DB issues — the migration is idempotent via its system-setting flag and rolls back cleanly.
  3. For large datasets, run during low traffic or increase query/statement timeouts.
  4. Verify the blocks table schema matches the expected version before running migrations.
  5. Check logs for the accompanying 'Unique IDs transaction rollback error' to confirm rollback succeeded; investigate lock contention if not.

Example fix

// before
err := store.RunUniqueIDsMigration()
// after
if err := store.RunUniqueIDsMigration(); err != nil {
	var dbErr *pq.Error
	if errors.As(errors.Unwrap(err), &dbErr) && dbErr.Code.Class() == "40" {
		log.Println("transient/timeout DB error, safe to retry", err)
	}
	log.Error(err)
}
Defensive patterns

Strategy: retry

Validate before calling

var count int
row := db.QueryRow("SELECT COUNT(*) FROM blocks")
if err := row.Scan(&count); err != nil { /* schema/connectivity problem — fix before migrating */ }

Type guard

func isDuplicateBlocksQueryError(err error) bool {
	return err != nil && strings.Contains(err.Error(), "cannot get blocks with same ID")
}

Try / catch

err := store.RunUniqueIDsMigration()
if err != nil {
	if isDuplicateBlocksQueryError(err) {
		// txn was rolled back by the store; raise statement_timeout, then retry
		log.Println("retryable migration query failure:", errors.Unwrap(err))
	}
	return err
}

Prevention

When it happens

Trigger: Running the unique-IDs migration when the duplicate-detection query fails: missing blocks table columns, DB connection loss mid-transaction, query timeout on very large block tables, or lock contention with concurrent writes.

Common situations: Large Focalboard instances (millions of blocks) timing out on the scan; database upgraded without the columns this migration expects; server started while another process holds locks on the Blocks table; connection pool exhausted during startup migrations.

Related errors


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