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

  1. Skip pools where structs.NodePool.IsBuiltIn() returns true before issuing the delete
  2. Filter out built-in pool names (default, all) in client-side tooling before calling the node-pool delete API
  3. 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

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


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/63710866761d5b7e. Report an issue: GitHub.