{"record":{"id":"048c8f9276ef3d17","repo":"mattermost-community/focalboard","slug":"cannot-mark-migration-as-completed-w","errorCode":null,"errorMessage":"cannot mark migration as completed: %w","messagePattern":"cannot mark migration as completed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/services/store/sqlstore/data_migrations.go","lineNumber":124,"sourceCode":"\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\"))\n\t\t\t\t}\n\t\t\t\treturn fmt.Errorf(\"cannot replace blockID %s: %w\", block.ID, err)\n\t\t\t}\n\t\t}\n\t}\n\n\tif err := s.setSystemSetting(tx, UniqueIDsMigrationKey, strconv.FormatBool(true)); 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\", \"setSystemSetting\"))\n\t\t}\n\t\treturn fmt.Errorf(\"cannot mark migration as completed: %w\", err)\n\t}\n\n\tif err := tx.Commit(); err != nil {\n\t\treturn fmt.Errorf(\"cannot commit unique IDs transaction: %w\", err)\n\t}\n\n\ts.logger.Debug(\"Unique IDs migration finished successfully\")\n\treturn nil\n}\n\n// RunCategoryUUIDIDMigration takes care of deriving the categories\n// from the boards and its memberships. The name references UUID\n// because of the preexisting purpose of this migration, and has been\n// preserved for compatibility with already migrated instances.\nfunc (s *SQLStore) RunCategoryUUIDIDMigration() error {\n\tsetting, err := s.GetSystemSetting(CategoryUUIDIDMigrationKey)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"cannot get migration state: %w\", err)","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/mattermost-community/focalboard/blob/a84bbb65e32edf972856b329417096ac413518e9/server/services/store/sqlstore/data_migrations.go#L106-L142","documentation":"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'.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nSET GLOBAL wait_timeout = 28800; -- default, too short for long txns\n// after\nSET GLOBAL wait_timeout = 86400;\nSET GLOBAL max_allowed_packet = 64M;\n-- then re-run: store.RunUniqueIDsMigration()","handlingStrategy":"retry","validationCode":"var canWrite bool\nif err := db.QueryRow(\"SELECT has_table_privilege('system_settings','INSERT','current_user')\").Scan(&canWrite); err != nil || !canWrite { /* grant write before migrating */ }","typeGuard":"func isMigrationFinalizeError(err error) bool {\n\treturn err != nil && strings.Contains(err.Error(), \"cannot mark migration as completed\")\n}","tryCatchPattern":"err := store.RunUniqueIDsMigration()\nif err != nil {\n\tif isMigrationFinalizeError(err) {\n\t\t// store already rolled back; fix timeouts/permissions and retry safely\n\t\tlog.Println(\"migration rolled back, retryable:\", errors.Unwrap(err))\n\t}\n\treturn err\n}","preventionTips":["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"],"tags":["database","migration","transaction","system-settings","focalboard"],"backgroundTag":"data-migration-finalize-failed","analyzedSha":"a84bbb65e32edf972856b329417096ac413518e9","analyzedAt":"2026-08-30T09:22:20.720Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}