dgraph-io/dgraph · error
another restore operation is already running
Error message
another restore operation is already running
What it means
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.
Source
Thrown at worker/draft.go:147
// If any other operation is running, this would error out. This error can
// be safely ignored because rollups will resume once that other task is done.
_, _ = n.startTask(opRollup)
}
}
closer := z.NewCloser(1)
switch id {
case opRollup:
if len(n.ops) > 0 {
return nil, errors.Errorf("another operation is already running")
}
go posting.IncrRollup.Process(closer, State.GetTimestamp)
case opRestore:
// Restores cancel all other operations, except for other restores since
// only one restore operation should be active any given moment.
for otherId, otherOp := range n.ops {
if otherId == opRestore {
return nil, errors.Errorf("another restore operation is already running")
}
// Remove from map and signal the closer to cancel the operation.
delete(n.ops, otherId)
otherOp.SignalAndWait()
}
case opBackup:
// Backup cancels all other operations, except for other backups since
// only one backup operation should be active any given moment. Also, indexing at higher
// timestamp can also run concurrently with backup.
for otherId, otherOp := range n.ops {
if otherId == opBackup {
return nil, errors.Errorf("another backup operation is already running")
}
// Remove from map and signal the closer to cancel the operation.
delete(n.ops, otherId)
otherOp.SignalAndWait()
}
case opIndexing:View on GitHub (pinned to 759e242be6)
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).
Example fix
// before: unconditional retry loop re-fires restore
for {
client.Restore(file)
}
// after: run restore only if none is active
if err := client.Restore(file); err != nil && strings.Contains(err.Error(), "another restore operation") {
// poll status and wait instead of retrying immediately
} Defensive patterns
Strategy: retry
Validate before calling
if restoreInProgress(cluster) { throw new Skip("restore already running") } Try / catch
try { await client.restore(file) } catch (e) {
if (e.message.includes('another restore operation')) {
await pollRestoreStatus()
} else { throw e }
} Prevention
- 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.
When it happens
Trigger: Calling startTask(opRestore) (POST /restore or restore gRPC) while a previous restore in the same group is still in n.ops.
Common situations: 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.
Related errors
- another operation is already running
- another backup operation is already running
- operation %s is already running
- Pending transactions found. Please retry operation
- while retrieving manifests
AI-assisted analysis of dgraph-io/dgraph@759e242be6 (2026-09-01).
Data as JSON: /api/errors/e39e84de0315f2cd.
Report an issue: GitHub.