{"record":{"id":"ee7a9c2892549e3e","repo":"ory/hydra","slug":"backup-finish-failed","errorCode":null,"errorMessage":"backup.Finish failed","messagePattern":"backup\\.Finish failed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"oryx/popx/migrator.go","lineNumber":121,"sourceCode":"\t\t\treturn errors.Errorf(\"driver %T does not support online restore\", driverConn)\n\t\t}\n\t\t// See: https://sqlite.org/backup.html .\n\t\tbackup, err := restorer.NewRestore(srcPath)\n\t\tif err != nil {\n\t\t\treturn errors.Wrap(err, \"NewRestore failed\")\n\t\t}\n\t\t// Step(-1) copies all remaining pages in one call.\n\t\tfor {\n\t\t\tmore, stepErr := backup.Step(-1)\n\t\t\tif stepErr != nil {\n\t\t\t\t_ = backup.Finish()\n\t\t\t\treturn errors.Wrap(stepErr, \"backup.Step failed\")\n\t\t\t}\n\t\t\tif !more {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\treturn errors.Wrap(backup.Finish(), \"backup.Finish failed\")\n\t})\n}\n\n// UpTo runs up to step \"up\" migrations and applies them to the database.\n// If step <= 0 all pending migrations are run.\nfunc (mb *MigrationBox) UpTo(ctx context.Context, step int) (applied int, err error) {\n\tctx, span := startSpan(ctx, MigrationUpOpName, trace.WithAttributes(attribute.Int(\"step\", step)))\n\tdefer otelx.End(span, &err)\n\n\tc := mb.c.WithContext(ctx)\n\n\tnewDbFileName, isOnDiskSQLite := sqliteFilePath(mb.c.URL())\n\tisSQLite := mb.c.Dialect.Name() == \"sqlite3\"\n\tisOnDiskSQLite = isSQLite && isOnDiskSQLite\n\n\t// For test SQLite databases, try to restore a pre-migrated template\n\t// using SQLite's online backup API. The restore streams pages directly into\n\t// the open connection, so mb.c stays valid throughout and any holders of","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/ory/hydra/blob/4174065ffb052799890f7480f5360a877a67ffc1/oryx/popx/migrator.go#L103-L139","documentation":"Returned when backup.Finish() reports an error after the SQLite online-restore page copy loop completed. Finish releases backup resources; failure here usually signals an I/O error or that the step loop ended abnormally earlier, and the error is wrapped for diagnosis.","triggerScenarios":"backup.Finish returns non-nil — typically when the backup was not fully completed (Step loop exited early) or the destination connection encountered an error finalizing the copy.","commonSituations":"Destination DB locked by another connection at finalization; earlier step errors leaving the backup in a bad state; driver-level failure committing the restored pages.","solutions":["Read the wrapped Finish error for the SQLite result code","Ensure no other connections hold locks on the destination database during restore","Verify the Step loop completed (more == false) before Finish — fix any early-break logic","Retry the restore on an idle database"],"exampleFix":"// before\nclose other app connections then restore\n// after\n_ = db.Close() // ensure idle\ndb, _ = sql.Open(\"sqlite\", dsn)\nerr := restoreSQLiteOnline(ctx, db, srcPath)","handlingStrategy":"retry","validationCode":"// Ensure no other connections hold the destination DB\nif err := db.PingContext(ctx); err != nil { return err }","typeGuard":null,"tryCatchPattern":"if err := restoreSQLiteOnline(ctx, db, srcPath); err != nil {\n  if strings.Contains(err.Error(), \"backup.Finish failed\") {\n    // close other connections, then retry once\n    return retryRestore(ctx, db, srcPath)\n  }\n  return err\n}","preventionTips":["Run restores with exclusive access to the destination database","Let the Step loop run to completion (more == false) before Finish","Keep the restore connection alive until Finish returns","Log the unwrapped SQLite result code for diagnostics"],"tags":["sqlite","go","backup"],"backgroundTag":"sqlite-backup-failed","analyzedSha":"4174065ffb052799890f7480f5360a877a67ffc1","analyzedAt":"2026-09-03T14:52:41.581Z","contentChangedAt":"2026-09-03T14:52:41.581Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}