hashicorp/nomad · error
deleting node pool %q is not allowed
Error message
deleting node pool %q is not allowed
What it means
Nomad's state store refuses to delete built-in node pools (e.g. 'default', 'all'). Node pools marked IsBuiltIn() are foundational to cluster operation, so deleteNodePoolTxn rejects the delete with this error before touching the table. It is a deliberate policy guard, not an infrastructure failure.
Source
Thrown at nomad/state/state_store_node_pools.go:210
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
- Skip pools where structs.NodePool.IsBuiltIn() returns true before issuing the delete
- Filter out built-in pool names (default, all) in client-side tooling before calling the node-pool delete API
- If a custom pool was misconfigured as built-in, it is not — built-ins are hard-coded; recreate your workload pool under a non-built-in name
Example fix
// before
for _, pool := range pools {
if err := client.NodePools().Delete(pool.Name, nil); err != nil { ... }
}
// after
for _, pool := range pools {
if structs.NewNodePool(pool.Name).IsBuiltIn() {
continue // skip built-in pools
}
if err := client.NodePools().Delete(pool.Name, nil); err != nil { ... }
} Defensive patterns
Strategy: validation
Validate before calling
func canDeleteNodePool(name string) bool {
switch structs.NodePoolDefault, structs.NodePoolAll { // built-in names
case name:
return false
}
return true
}
if !canDeleteNodePool(poolName) {
return fmt.Errorf("refusing to delete built-in node pool %q", poolName)
} Try / catch
if err := client.NodePools().Delete(name, nil); err != nil {
if strings.Contains(err.Error(), "is not allowed") {
// built-in pool: treat as no-op / skip
return nil
}
return err
} Prevention
- Always check pool.IsBuiltIn() (or compare against 'default'/'all') before deleting
- In bulk-cleanup scripts, filter the node pool list first
- Treat this error as expected control flow, not a retryable fault
When it happens
Trigger: Calling StateStore.DeleteNodePools (via the NodePools.Delete RPC / `nomad node pool delete`) for a built-in pool such as 'default' or 'all'.
Common situations: Operators scripting node pool cleanup accidentally targeting 'default'; tools that iterate all node pools and attempt deletion of each; automation that deletes pools before checking pool origin.
Related errors
- job_submission requires a namespace
- job_submission requires a jobID
- validating volume %q against state failed: %v
- unknown new job status %q
- one-time token lookup failed: missing secret
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/63710866761d5b7e.
Report an issue: GitHub.