hashicorp/nomad · error
eval %s is not safe to delete
Error message
eval %s is not safe to delete
What it means
Returned by Evaluations.Delete when EvalIsUserDeleteSafe determines that one of the requested evals must not be user-deleted (e.g. it is tied to a live scheduling/deployment flow). The entire delete is aborted to keep eval accounting consistent.
Source
Thrown at nomad/eval_endpoint.go:526
// Iterate the evaluations and ensure they are safe to delete. It is
// possible passed evals are not safe to delete and would make Nomads state
// a little wonky. The nature of the RPC return error, means a single
// unsafe eval ID fails the whole call.
for _, evalID := range args.EvalIDs {
evalInfo, err := serverStateSnapshot.EvalByID(ws, evalID)
if err != nil {
return fmt.Errorf("failed to lookup eval: %v", err)
}
if evalInfo == nil {
return errors.New("eval not found")
}
ok, err := serverStateSnapshot.EvalIsUserDeleteSafe(ws, evalInfo)
if err != nil {
return err
}
if !ok {
return fmt.Errorf("eval %s is not safe to delete", evalInfo.ID)
}
count++
}
// Generate the Raft request object using the reap request object. This
// avoids adding new Raft messages types and follows the existing reap
// flow.
raftReq := structs.EvalReapRequest{
Evals: args.EvalIDs,
UserInitiated: true,
WriteRequest: args.WriteRequest,
}
// Update via Raft.
_, index, err := e.srv.raftApply(structs.EvalDeleteRequestType, &raftReq)
if err != nil {
return err
}View on GitHub (pinned to 482b49bf1a)
Solutions
- Wait for associated deployments/allocations to finish, or cancel them first, then retry
- Filter target evals to terminal states (complete/cancelled) before deleting
- Delete the evals via filter with the broker paused so safety checks apply consistently
Example fix
// before client.Evaluations().Delete(allEvalIDs, nil, "") // after safeIDs := filter(terminalEvalsOnly) client.Evaluations().Delete(safeIDs, nil, "")
Defensive patterns
Strategy: retry
Validate before calling
eval, _, _ := client.Evaluations().Get(id)
if eval != nil && eval.Status != "complete" && eval.Status != "cancelled" {
return fmt.Errorf("eval %s not terminal; skip delete", id)
} Try / catch
err := deleteEvals(ids)
if err != nil && strings.Contains(err.Error(), "is not safe to delete") {
time.Sleep(backoff) // wait for deployments to settle, then retry
return retry(ids)
} Prevention
- Only delete evals in terminal states
- Pause eval broker and wait for active deployments to finish before cleanup
- Exclude evals tied to in-progress deployments from bulk deletes
When it happens
Trigger: Calling eval delete with an eval ID whose EvalIsUserDeleteSafe check returns false — typically evals still referenced by active allocations, deployments, or newly-created evals not yet eligible for user deletion.
Common situations: Attempting to clean up evals while a deployment is in progress; deleting evals that the blocked-eval broker or deployment watcher still owns; scripting bulk deletes over all evals without filtering to safe ones.
Related errors
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/d856d4b4d43673f3.
Report an issue: GitHub.