hashicorp/nomad · error
node pool %s not found
Error message
node pool %s not found
What it means
deleteNodePoolTxn fails when the named node pool does not exist in the node_pools table. The lookup `txn.First(TableNodePools, "id", name)` returns nil, so the delete is rejected with this message rather than silently succeeding. Note deletion of existing non-built-in pools proceeds afterward only if the pool is not built-in and has no nodes.
Source
Thrown at nomad/state/state_store_node_pools.go:203
}
}
// Update index table.
if err := txn.Insert("index", &IndexEntry{TableNodePools, index}); err != nil {
return fmt.Errorf("index update failed: %w", err)
}
return txn.Commit()
}
func (s *StateStore) deleteNodePoolTxn(txn *txn, index uint64, name string) error {
// Check if node pool exists.
existing, err := txn.First(TableNodePools, "id", name)
if err != nil {
return fmt.Errorf("node pool lookup failed: %w", err)
}
if existing == nil {
return fmt.Errorf("node pool %s not found", name)
}
pool := existing.(*structs.NodePool)
// Prevent deletion of built-in node pools.
if pool.IsBuiltIn() {
return fmt.Errorf("deleting node pool %q is not allowed", pool.Name)
}
// Delete node pool.
if err := txn.Delete(TableNodePools, pool); err != nil {
return fmt.Errorf("node pool deletion failed: %w", err)
}
return nil
}
View on GitHub (pinned to 482b49bf1a)
Solutions
- Verify the pool name with `nomad node pool list` and correct any typo in the delete request.
- Add an existence check (GET the node pool or txn.First on the table) before deleting to make automation idempotent.
- Treat 'not found' as success in idempotent cleanup scripts and skip the delete.
- Recreate the pool first if it was deleted accidentally and the delete is part of a larger transaction.
Example fix
// before
stateStore.DeleteNodePools(idx, []string{"prod-pool"}, nil) // may not exist
// after
if pool, _ := stateStore.NodePoolByName(nil, "prod-pool"); pool != nil {
stateStore.DeleteNodePools(idx, []string{"prod-pool"}, nil)
} Defensive patterns
Strategy: validation
Validate before calling
existing, err := s.NodePoolByName(nil, name)
if err != nil {
return err
}
if existing == nil {
return nil // already gone; treat as idempotent success
}
err = s.DeleteNodePools(idx, []string{name}, nil) Type guard
func poolExists(s *state.StateStore, name string) bool {
p, err := s.NodePoolByName(nil, name)
return err == nil && p != nil
} Try / catch
err := s.DeleteNodePools(idx, []string{name}, nil)
if err != nil && strings.Contains(err.Error(), "not found") {
return nil // idempotent: pool already deleted
} Prevention
- List pools with `nomad node pool list` and confirm the exact name before deleting.
- Make cleanup scripts idempotent by treating not-found as success.
- Use a single serialized process for pool lifecycle to avoid double-deletes.
- Verify environment (cluster/namespace) before delete to avoid cross-env name mismatches.
When it happens
Trigger: Calling DeleteNodePools with a name that was never created or has already been deleted — e.g. `nomad node pool delete <name>` for a typo'd name, or an automation deleting pools idempotently without an existence check.
Common situations: Typo'd pool name in CLI or Terraform config; racing deletions where two processes delete the same pool; environment drift where the pool exists in one cluster but not another (staging vs prod).
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- eval not found
- variable doesn't exist
- No node pool with prefix %q found
- unknown deployment %q
- node pool %q does not exist
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/4c1b00ce06dd98a8.
Report an issue: GitHub.