{"record":{"id":"e39e84de0315f2cd","repo":"dgraph-io/dgraph","slug":"another-restore-operation-is-already-running","errorCode":null,"errorMessage":"another restore operation is already running","messagePattern":"another restore operation is already running","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"worker/draft.go","lineNumber":147,"sourceCode":"\t\t\t// If any other operation is running, this would error out. This error can\n\t\t\t// be safely ignored because rollups will resume once that other task is done.\n\t\t\t_, _ = n.startTask(opRollup)\n\t\t}\n\t}\n\n\tcloser := z.NewCloser(1)\n\tswitch id {\n\tcase opRollup:\n\t\tif len(n.ops) > 0 {\n\t\t\treturn nil, errors.Errorf(\"another operation is already running\")\n\t\t}\n\t\tgo posting.IncrRollup.Process(closer, State.GetTimestamp)\n\tcase opRestore:\n\t\t// Restores cancel all other operations, except for other restores since\n\t\t// only one restore operation should be active any given moment.\n\t\tfor otherId, otherOp := range n.ops {\n\t\t\tif otherId == opRestore {\n\t\t\t\treturn nil, errors.Errorf(\"another restore operation is already running\")\n\t\t\t}\n\t\t\t// Remove from map and signal the closer to cancel the operation.\n\t\t\tdelete(n.ops, otherId)\n\t\t\totherOp.SignalAndWait()\n\t\t}\n\tcase opBackup:\n\t\t// Backup cancels all other operations, except for other backups since\n\t\t// only one backup operation should be active any given moment. Also, indexing at higher\n\t\t// timestamp can also run concurrently with backup.\n\t\tfor otherId, otherOp := range n.ops {\n\t\t\tif otherId == opBackup {\n\t\t\t\treturn nil, errors.Errorf(\"another backup operation is already running\")\n\t\t\t}\n\t\t\t// Remove from map and signal the closer to cancel the operation.\n\t\t\tdelete(n.ops, otherId)\n\t\t\totherOp.SignalAndWait()\n\t\t}\n\tcase opIndexing:","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/dgraph-io/dgraph/blob/759e242be62c91f8d084da06ad0c8d21256d9c07/worker/draft.go#L129-L165","documentation":"Raised in startTaskAtTs (worker/draft.go:147) when a restore is requested while another restore is already running. Unlike other operations, restore refuses to run concurrently with another restore — restores cancel other operation kinds but never each other.","triggerScenarios":"Calling startTask(opRestore) (POST /restore or restore gRPC) while a previous restore in the same group is still in n.ops.","commonSituations":"Double-clicking a restore UI action; retrying a slow restore without checking its progress; automated pipelines re-triggering restore after a timeout that did not actually stop the first restore.","solutions":["Wait for the current restore to complete; poll the restore status instead of re-issuing restore.","If the first restore is truly wedged, restart the node to clear it, then restore again.","Serialize restore jobs in your deployment pipeline (locks/semaphore per cluster)."],"exampleFix":"// before: unconditional retry loop re-fires restore\nfor {\n    client.Restore(file)\n}\n// after: run restore only if none is active\nif err := client.Restore(file); err != nil && strings.Contains(err.Error(), \"another restore operation\") {\n    // poll status and wait instead of retrying immediately\n}","handlingStrategy":"retry","validationCode":"if restoreInProgress(cluster) { throw new Skip(\"restore already running\") }","typeGuard":null,"tryCatchPattern":"try { await client.restore(file) } catch (e) {\n  if (e.message.includes('another restore operation')) {\n    await pollRestoreStatus()\n  } else { throw e }\n}","preventionTips":["Use a distributed lock around restore invocations.","Make restore jobs idempotent and status-driven, not fire-and-retry.","Never re-trigger restore from timeout handlers without checking status."],"tags":["dgraph","restore","operation-conflict"],"backgroundTag":"operation-already-running","analyzedSha":"759e242be62c91f8d084da06ad0c8d21256d9c07","analyzedAt":"2026-09-01T14:42:12.034Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}