hashicorp/nomad · error

volume max claims reached

Error message

volume max claims reached

What it means

ErrCSIVolumeMaxClaims is returned when a volume claim would exceed the allowed number of concurrent claims — Nomad permits only one writer (or the configured ReadSched/WriteSched reader count) per volume. The state store rejects the Claim RPC when the volume's capacity for concurrent access is exhausted.

Source

Thrown at nomad/structs/errors.go:91

	// Keep the two in sync.
	ErrResultPaginatorCreation = errors.New(errResultPaginatorCreation)

	ErrUnknownNode = errors.New(ErrUnknownNodePrefix)

	ErrDeploymentTerminalNoCancel    = errors.New(errDeploymentTerminalNoCancel)
	ErrDeploymentTerminalNoFail      = errors.New(errDeploymentTerminalNoFail)
	ErrDeploymentTerminalNoPause     = errors.New(errDeploymentTerminalNoPause)
	ErrDeploymentTerminalNoPromote   = errors.New(errDeploymentTerminalNoPromote)
	ErrDeploymentTerminalNoResume    = errors.New(errDeploymentTerminalNoResume)
	ErrDeploymentTerminalNoUnblock   = errors.New(errDeploymentTerminalNoUnblock)
	ErrDeploymentTerminalNoRun       = errors.New(errDeploymentTerminalNoRun)
	ErrDeploymentTerminalNoSetHealth = errors.New(errDeploymentTerminalNoSetHealth)
	ErrDeploymentRunningNoUnblock    = errors.New(errDeploymentRunningNoUnblock)

	ErrCSIClientRPCIgnorable  = errors.New("CSI client error (ignorable)")
	ErrCSIClientRPCRetryable  = errors.New("CSI client error (retryable)")
	ErrCSIVolumeMaxClaims     = errors.New("volume max claims reached")
	ErrCSIVolumeUnschedulable = errors.New("volume is currently unschedulable")
	ErrCSIPluginInUse         = errors.New("plugin in use")
)

// IsErrNoLeader returns whether the error is due to there being no leader.
func IsErrNoLeader(err error) bool {
	return err != nil && strings.Contains(err.Error(), errNoLeader)
}

// IsErrNoRegionPath returns whether the error is due to there being no path to
// the given region.
func IsErrNoRegionPath(err error) bool {
	return err != nil && strings.Contains(err.Error(), errNoRegionPath)
}

// IsErrTokenNotFound returns whether the error is due to the passed token not
// being resolvable.
func IsErrTokenNotFound(err error) bool {
	return err != nil && strings.Contains(err.Error(), errTokenNotFound)

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Release the existing claim (stop the other allocation, or use CSIVolume.Unclaim / 'nomad volume detach <vol> <alloc>') and retry
  2. Configure the volume as multi-writer (csi_plugin/mount_flags with MULTI_NODE_MULTI_WRITER capability) if concurrent writes are truly intended — only if the storage backend supports it
  3. Use volume scaling/clone or a second volume so each writer has its own volume
  4. Verify no orphaned claims from crashed allocations exist (access mode/attachment status in 'nomad volume status') and clean them up

Example fix

// before
job uses volume "data" with access_mode = "single-node-writer" on 2 allocations
// after
# either run a single writer allocation, or declare the volume with
# capability MULTI_NODE_MULTI_WRITER and access_mode = "multi-node-multi-writer"
volume "data" {
  type   = "csi"
  source = "data"
  access_mode     = "multi-node-multi-writer"
  attachment_mode = "file-system"
}
Defensive patterns

Strategy: validation

Validate before calling

vol, _, _ := client.CSIVolumes().Info(volID, nil)
if vol != nil {
    for _, a := range vol.Allocations {
        if a.Mode == "single-node-writer" {
            return fmt.Errorf("volume %s write claim held by alloc %s", volID, a.ID)
        }
    }
}

Type guard

func volumeWritable(vol *api.CSIVolume) bool {
    return vol != nil && vol.WriteAllocs != nil && len(vol.WriteAllocs) == 0
}

Try / catch

err := claimVolume(volID, allocID)
if err != nil && strings.Contains(err.Error(), structs.ErrCSIVolumeMaxClaims.Error()) {
    return fmt.Errorf("volume %s already claimed; unclaim or provision another", volID)
}

Prevention

When it happens

Trigger: Calling CSIVolume.Claim with a write claim while another allocation already holds the write claim, or exceeding max reader claims; observed in isRetryableClaimRPCError and volume claim endpoints/tests.

Common situations: Two jobs (or two allocations of one job) both request exclusive access to the same host volume; an old allocation's claim was not released (stale claim) so the new one is rejected; scaling a service that shares a single-writer volume.

Related errors


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