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

  1. Wait for the current restore to complete; poll the restore status instead of re-issuing restore.
  2. If the first restore is truly wedged, restart the node to clear it, then restore again.
  3. 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

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


AI-assisted analysis of dgraph-io/dgraph@759e242be6 (2026-09-01). Data as JSON: /api/errors/e39e84de0315f2cd. Report an issue: GitHub.