hashicorp/nomad · error

CSIPluginConfig PluginType must be one of 'node', 'controlle

Error message

CSIPluginConfig PluginType must be one of 'node', 'controller', or 'monolith', got: "%s"

What it means

Task.Validate checks CSIPluginConfig.Type against CSIPluginTypeIsValid, which accepts only 'node', 'controller', or 'monolith'. Any other string (or empty) fails this error. The plugin type tells Nomad which capabilities (controller/node) the plugin exposes.

Source

Thrown at nomad/structs/structs.go:8413

	// Validation for volumes
	for idx, vm := range t.VolumeMounts {
		if _, ok := tg.Volumes[vm.Volume]; !ok {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("Volume Mount (%d) references undefined volume %s", idx, vm.Volume))
		}

		if err := vm.Validate(); err != nil {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("Volume Mount (%d) is invalid: \"%w\"", idx, err))
		}
	}

	// Validate CSI Plugin Config
	if t.CSIPluginConfig != nil {
		if t.CSIPluginConfig.ID == "" {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("CSIPluginConfig must have a non-empty PluginID"))
		}

		if !CSIPluginTypeIsValid(t.CSIPluginConfig.Type) {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("CSIPluginConfig PluginType must be one of 'node', 'controller', or 'monolith', got: \"%s\"", t.CSIPluginConfig.Type))
		}

		if t.CSIPluginConfig.StagePublishBaseDir != "" && t.CSIPluginConfig.MountDir != "" &&
			helper.IsSubdirectory(t.CSIPluginConfig.MountDir, t.CSIPluginConfig.StagePublishBaseDir) {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("CSIPluginConfig StagePublishBaseDir must not be a subdirectory of MountDir, got: StagePublishBaseDir=\"%s\" MountDir=\"%s\"", t.CSIPluginConfig.StagePublishBaseDir, t.CSIPluginConfig.MountDir))
		}

		// TODO: Investigate validation of the PluginMountDir. Not much we can do apart from check IsAbs until after we understand its execution environment though :(
	}

	// Validate default Identity
	if t.Identity != nil {
		if err := t.Identity.Validate(); err != nil {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("Identity %q is invalid: %w", t.Identity.Name, err))
		}
	}

	// Validate Identities

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Change type to one of: "node", "controller", or "monolith"
  2. Use "monolith" if the plugin provides both controller and node capabilities
  3. Check the plugin's documentation for which CSI controller/node capabilities it implements and pick accordingly

Example fix

// before
csi_plugin {
  id   = "aws-efs0"
  type = "Node"
}
// after
csi_plugin {
  id   = "aws-efs0"
  type = "node"
}
Defensive patterns

Strategy: validation

Validate before calling

var validCSIPluginTypes = map[string]bool{"node": true, "controller": true, "monolith": true}
if cfg != nil && !validCSIPluginTypes[cfg.Type] {
    return fmt.Errorf("csi_plugin type %q must be node, controller, or monolith", cfg.Type)
}

Type guard

func csiPluginTypeValid(t string) bool {
    return t == "node" || t == "controller" || t == "monolith"
}

Prevention

When it happens

Trigger: csi_plugin block with type set to a misspelled or unsupported value like "plugins", "Node", "storage", or omitted entirely (empty string) in a task's csi_plugin config.

Common situations: Typos or wrong casing in the jobspec type argument; copying examples from other orchestrators (Kubernetes/CSIDriver) that use different type vocabularies; older Nomad jobspecs written before the field was enforced.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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