{"record":{"id":"9c6382be663b005a","repo":"mattermost-community/focalboard","slug":"cannot-get-blocks-with-same-id-w","errorCode":null,"errorMessage":"cannot get blocks with same ID: %w","messagePattern":"cannot get blocks with same ID: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/services/store/sqlstore/data_migrations.go","lineNumber":95,"sourceCode":"\n\t// If the migration is already completed, do not run it again.\n\tif hasAlreadyRun, _ := strconv.ParseBool(setting); hasAlreadyRun {\n\t\treturn nil\n\t}\n\n\ts.logger.Debug(\"Running Unique IDs migration\")\n\n\ttx, txErr := s.db.BeginTx(context.Background(), nil)\n\tif txErr != nil {\n\t\treturn txErr\n\t}\n\n\tblocks, err := s.getBlocksWithSameID(tx)\n\tif err != nil {\n\t\tif rollbackErr := tx.Rollback(); rollbackErr != nil {\n\t\t\ts.logger.Error(\"Unique IDs transaction rollback error\", mlog.Err(rollbackErr), mlog.String(\"methodName\", \"getBlocksWithSameID\"))\n\t\t}\n\t\treturn fmt.Errorf(\"cannot get blocks with same ID: %w\", err)\n\t}\n\n\tblocksByID := map[string][]*model.Block{}\n\tfor _, block := range blocks {\n\t\tblocksByID[block.ID] = append(blocksByID[block.ID], block)\n\t}\n\n\tfor _, blocks := range blocksByID {\n\t\tfor i, block := range blocks {\n\t\t\tif i == 0 {\n\t\t\t\t// do nothing for the first ID, only updating the others\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tnewID := utils.NewID(model.BlockType2IDType(block.Type))\n\t\t\tif err := s.replaceBlockID(tx, block.ID, newID, block.WorkspaceID); err != nil {\n\t\t\t\tif rollbackErr := tx.Rollback(); rollbackErr != nil {\n\t\t\t\t\ts.logger.Error(\"Unique IDs transaction rollback error\", mlog.Err(rollbackErr), mlog.String(\"methodName\", \"replaceBlockID\"))","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/mattermost-community/focalboard/blob/a84bbb65e32edf972856b329417096ac413518e9/server/services/store/sqlstore/data_migrations.go#L77-L113","documentation":"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'.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check the wrapped cause for the exact DB failure (timeout, connection, schema).","Rerun after resolving transient DB issues — the migration is idempotent via its system-setting flag and rolls back cleanly.","For large datasets, run during low traffic or increase query/statement timeouts.","Verify the blocks table schema matches the expected version before running migrations.","Check logs for the accompanying 'Unique IDs transaction rollback error' to confirm rollback succeeded; investigate lock contention if not."],"exampleFix":"// before\nerr := store.RunUniqueIDsMigration()\n// after\nif err := store.RunUniqueIDsMigration(); err != nil {\n\tvar dbErr *pq.Error\n\tif errors.As(errors.Unwrap(err), &dbErr) && dbErr.Code.Class() == \"40\" {\n\t\tlog.Println(\"transient/timeout DB error, safe to retry\", err)\n\t}\n\tlog.Error(err)\n}","handlingStrategy":"retry","validationCode":"var count int\nrow := db.QueryRow(\"SELECT COUNT(*) FROM blocks\")\nif err := row.Scan(&count); err != nil { /* schema/connectivity problem — fix before migrating */ }","typeGuard":"func isDuplicateBlocksQueryError(err error) bool {\n\treturn err != nil && strings.Contains(err.Error(), \"cannot get blocks with same ID\")\n}","tryCatchPattern":"err := store.RunUniqueIDsMigration()\nif err != nil {\n\tif isDuplicateBlocksQueryError(err) {\n\t\t// txn was rolled back by the store; raise statement_timeout, then retry\n\t\tlog.Println(\"retryable migration query failure:\", errors.Unwrap(err))\n\t}\n\treturn err\n}","preventionTips":["Run migrations during low-traffic windows on large datasets","Increase statement/query timeouts before migrating big block tables","Confirm the blocks table schema matches the app version","Watch logs for rollback errors to confirm clean rollback before retrying"],"tags":["database","migration","transaction","focalboard"],"backgroundTag":"data-migration-query-failed","analyzedSha":"a84bbb65e32edf972856b329417096ac413518e9","analyzedAt":"2026-08-30T09:22:20.720Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}