{"record":{"id":"35216ced844d3ca6","repo":"hashicorp/nomad","slug":"csipluginconfig-stagepublishbasedir-must-not-be-a","errorCode":null,"errorMessage":"CSIPluginConfig StagePublishBaseDir must not be a subdirectory of MountDir, got: StagePublishBaseDir=\"%s\" MountDir=\"%s\"","messagePattern":"CSIPluginConfig StagePublishBaseDir must not be a subdirectory of MountDir, got: StagePublishBaseDir=\"(.+?)\" MountDir=\"(.+?)\"","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/structs.go","lineNumber":8418,"sourceCode":"\n\t\tif err := vm.Validate(); err != nil {\n\t\t\tmErr.Errors = append(mErr.Errors, fmt.Errorf(\"Volume Mount (%d) is invalid: \\\"%w\\\"\", idx, err))\n\t\t}\n\t}\n\n\t// Validate CSI Plugin Config\n\tif t.CSIPluginConfig != nil {\n\t\tif t.CSIPluginConfig.ID == \"\" {\n\t\t\tmErr.Errors = append(mErr.Errors, fmt.Errorf(\"CSIPluginConfig must have a non-empty PluginID\"))\n\t\t}\n\n\t\tif !CSIPluginTypeIsValid(t.CSIPluginConfig.Type) {\n\t\t\tmErr.Errors = append(mErr.Errors, fmt.Errorf(\"CSIPluginConfig PluginType must be one of 'node', 'controller', or 'monolith', got: \\\"%s\\\"\", t.CSIPluginConfig.Type))\n\t\t}\n\n\t\tif t.CSIPluginConfig.StagePublishBaseDir != \"\" && t.CSIPluginConfig.MountDir != \"\" &&\n\t\t\thelper.IsSubdirectory(t.CSIPluginConfig.MountDir, t.CSIPluginConfig.StagePublishBaseDir) {\n\t\t\tmErr.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))\n\t\t}\n\n\t\t// TODO: Investigate validation of the PluginMountDir. Not much we can do apart from check IsAbs until after we understand its execution environment though :(\n\t}\n\n\t// Validate default Identity\n\tif t.Identity != nil {\n\t\tif err := t.Identity.Validate(); err != nil {\n\t\t\tmErr.Errors = append(mErr.Errors, fmt.Errorf(\"Identity %q is invalid: %w\", t.Identity.Name, err))\n\t\t}\n\t}\n\n\t// Validate Identities\n\tfor _, wid := range t.Identities {\n\t\t// Task.Canonicalize should move the default identity out of the Identities\n\t\t// slice, so if one is found that means it is a duplicate.\n\t\tif wid.Name == WorkloadIdentityDefaultName {\n\t\t\tmErr.Errors = append(mErr.Errors, fmt.Errorf(\"Duplicate default identities found\"))","sourceCodeStart":8400,"sourceCodeEnd":8436,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/structs.go#L8400-L8436","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Move stage_publish_base_dir outside mount_dir (e.g. /var/nomad/staging vs /var/nomad/plugin)","If both were accidentally the same tree, set stage_publish_base_dir to a sibling directory","Leave stage_publish_base_dir empty to use the client's default staging location"],"exampleFix":"// before\ncsi_plugin {\n  id                    = \"aws-efs0\"\n  type                  = \"node\"\n  mount_dir             = \"/var/nomad/plugins\"\n  stage_publish_base_dir = \"/var/nomad/plugins/staging\"\n}\n// after\ncsi_plugin {\n  id                    = \"aws-efs0\"\n  type                  = \"node\"\n  mount_dir             = \"/var/nomad/plugins\"\n  stage_publish_base_dir = \"/var/nomad/csi-staging\"\n}","handlingStrategy":"validation","validationCode":"if cfg != nil && cfg.MountDir != \"\" && cfg.StagePublishBaseDir != \"\" {\n    if rel, _ := filepath.Rel(cfg.MountDir, cfg.StagePublishBaseDir); rel != \"..\" && !strings.HasPrefix(rel, \"..\") && rel != \".\" {\n        return errors.New(\"stage_publish_base_dir must not be inside mount_dir\")\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep staging/publish dirs as siblings of the plugin mount dir, never nested","Pick distinct top-level paths like /var/nomad/plugins and /var/nomad/csi-staging","Document node client csi_plugin paths in infrastructure config review checklists"],"tags":["nomad","csi","validation","filesystem"],"backgroundTag":"invalid-config-path","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}