hashicorp/nomad · error

CSIPluginConfig StagePublishBaseDir must not be a subdirecto

Error message

CSIPluginConfig StagePublishBaseDir must not be a subdirectory of MountDir, got: StagePublishBaseDir="%s" MountDir="%s"

What it means

Task.Validate rejects a csi_plugin config whose stage_publish_base_dir lies inside mount_dir (helper.IsSubdirectory check). Nesting staging/publishing scratch space inside the mounted plugin dir can cause mount recursion and corrupted paths, so Nomad forbids it.

Source

Thrown at nomad/structs/structs.go:8418

		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
	for _, wid := range t.Identities {
		// Task.Canonicalize should move the default identity out of the Identities
		// slice, so if one is found that means it is a duplicate.
		if wid.Name == WorkloadIdentityDefaultName {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("Duplicate default identities found"))

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Move stage_publish_base_dir outside mount_dir (e.g. /var/nomad/staging vs /var/nomad/plugin)
  2. If both were accidentally the same tree, set stage_publish_base_dir to a sibling directory
  3. Leave stage_publish_base_dir empty to use the client's default staging location

Example fix

// before
csi_plugin {
  id                    = "aws-efs0"
  type                  = "node"
  mount_dir             = "/var/nomad/plugins"
  stage_publish_base_dir = "/var/nomad/plugins/staging"
}
// after
csi_plugin {
  id                    = "aws-efs0"
  type                  = "node"
  mount_dir             = "/var/nomad/plugins"
  stage_publish_base_dir = "/var/nomad/csi-staging"
}
Defensive patterns

Strategy: validation

Validate before calling

if cfg != nil && cfg.MountDir != "" && cfg.StagePublishBaseDir != "" {
    if rel, _ := filepath.Rel(cfg.MountDir, cfg.StagePublishBaseDir); rel != ".." && !strings.HasPrefix(rel, "..") && rel != "." {
        return errors.New("stage_publish_base_dir must not be inside mount_dir")
    }
}

Prevention

When it happens

Trigger: csi_plugin block where both mount_dir and stage_publish_base_dir are set and stage_publish_base_dir is a subdirectory of (or equal to) mount_dir, e.g. mount_dir=/var/nomad/plugin and stage_publish_base_dir=/var/nomad/plugin/staging.

Common situations: Operators configuring node plugin csi_plugin blocks on the client pick a staging dir under the plugin mount dir for tidiness; copy-paste of the same base path into both fields.

Related errors


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