hashicorp/nomad · error

node pool cannot be updated

Error message

node pool cannot be updated

What it means

Like NodeID, a host volume's NodePool is immutable after creation. ValidateUpdate appends 'node pool cannot be updated' when the request supplies a non-empty NodePool that differs from existing.NodePool. This prevents volumes from silently moving between node pools while allocations reference them.

Source

Thrown at nomad/structs/host_volumes.go:192

func (hv *HostVolume) ValidateUpdate(existing *HostVolume) error {
	if existing == nil {
		return nil
	}

	var mErr *multierror.Error
	if len(existing.Allocations) > 0 {
		allocIDs := helper.ConvertSlice(existing.Allocations,
			func(a *AllocListStub) string { return a.ID })
		mErr = multierror.Append(mErr, fmt.Errorf(
			"cannot update a volume in use: claimed by allocs (%s)",
			strings.Join(allocIDs, ", ")))
	}

	if hv.NodeID != "" && hv.NodeID != existing.NodeID {
		mErr = multierror.Append(mErr, errors.New("node ID cannot be updated"))
	}
	if hv.NodePool != "" && hv.NodePool != existing.NodePool {
		mErr = multierror.Append(mErr, errors.New("node pool cannot be updated"))
	}

	if hv.RequestedCapacityMaxBytes > 0 &&
		hv.RequestedCapacityMaxBytes < existing.CapacityBytes {
		mErr = multierror.Append(mErr, fmt.Errorf(
			"capacity_max (%d) cannot be less than existing provisioned capacity (%d)",
			hv.RequestedCapacityMaxBytes, existing.CapacityBytes))
	}

	return mErr.ErrorOrNil()
}

const DefaultHostVolumePlugin = "default"

// CanonicalizeForCreate is called in the RPC handler to ensure we call client
// RPCs with correctly populated fields from the existing volume, even if the
// RPC request includes otherwise valid zero-values. This method should be
// called on request objects or a copy, never on a state store object directly.

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Keep node_pool identical to the existing volume's value in update requests
  2. Omit node_pool from the update payload (empty string bypasses the check)
  3. Create a new volume in the target node pool and migrate workloads instead
  4. Adjust automation to preserve immutable fields (node_id, node_pool) on update

Example fix

// before
update := existing.Copy()
update.NodePool = "gpu-pool" // not allowed

// after
update := existing.Copy()
// keep update.NodePool == existing.NodePool
update.RequestedCapacityMaxBytes = 200 * 1024 * 1024 * 1024
Defensive patterns

Strategy: validation

Validate before calling

if update.NodePool != "" && update.NodePool != existing.NodePool {
	return fmt.Errorf("node_pool is immutable (%s != %s)", existing.NodePool, update.NodePool)
}

Prevention

When it happens

Trigger: Calling ValidateUpdate on a host volume where hv.NodePool is non-empty and differs from the existing volume's NodePool.

Common situations: Renaming/organizing node pools and trying to move existing volumes into the new pool; multi-cluster tooling that stamps a pool name onto update specs; policy changes reassigning volumes across pools.

Related errors


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