{"record":{"id":"9c5d7164fdf8437a","repo":"argoproj/argo-workflows","slug":"refusing-to-stage-input-artifact-q-at-s-it-reso-9c5d71","errorCode":null,"errorMessage":"refusing to stage input artifact %q at %s: it resolves to %s which contains volume mount %q (%s), and clearing it would destroy the mounted volume; change the artifact path or volume mount so they do not overlap","messagePattern":"refusing to stage input artifact %q at (.+?): it resolves to (.+?) which contains volume mount %q \\((.+?)\\), and clearing it would destroy the mounted volume; change the artifact path or volume mount so they do not overlap","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/argoexec/commands/emissary.go","lineNumber":431,"sourceCode":"\t\t\t// symlink) reproduces the legacy SubPath mount's shadowing — but only\n\t\t\t// when it is safe. RemoveAll resolves symlinks in the parent chain, so\n\t\t\t// resolve the parent to find where the delete would actually land\n\t\t\t// (resolve the parent, not the final element, so an image symlink *at*\n\t\t\t// art.Path is just unlinked rather than followed). If that resolved\n\t\t\t// path overlaps a user-declared volume, clearing it would recurse into\n\t\t\t// and destroy a live PVC/hostPath/emptyDir, so refuse. Benign system\n\t\t\t// mounts (tmpfs /run, the overlay rootfs) are not declared user volumes\n\t\t\t// and so remain safe to shadow.\n\t\t\trealParent, evalErr := filepath.EvalSymlinks(filepath.Dir(dst))\n\t\t\tif evalErr != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to resolve parent of artifact path %q at %s: %w\", art.Name, dst, evalErr)\n\t\t\t}\n\t\t\tresolved := filepath.Join(realParent, filepath.Base(dst))\n\t\t\tif mnt := common.FindOverlappingVolume(tmpl, resolved); mnt != nil {\n\t\t\t\treturn fmt.Errorf(\"refusing to stage input artifact %q at %s: it resolves to %s inside volume mount %q (%s), and clearing it would destroy the mounted volume; change the artifact path or volume mount so they do not overlap\", art.Name, dst, resolved, mnt.Name, mnt.MountPath)\n\t\t\t}\n\t\t\tif mnt := common.FindVolumeMountNestedUnderPath(tmpl, resolved); mnt != nil {\n\t\t\t\treturn fmt.Errorf(\"refusing to stage input artifact %q at %s: it resolves to %s which contains volume mount %q (%s), and clearing it would destroy the mounted volume; change the artifact path or volume mount so they do not overlap\", art.Name, dst, resolved, mnt.Name, mnt.MountPath)\n\t\t\t}\n\t\t\tif rmErr := os.RemoveAll(dst); rmErr != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to clear existing path for artifact %q at %s: %w\", art.Name, dst, rmErr)\n\t\t\t}\n\t\t}\n\t\tif err := os.Symlink(src, dst); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to symlink input artifact %q (%s -> %s): %w\", art.Name, dst, src, err)\n\t\t}\n\t\tlogger.WithFields(logging.Fields{\"name\": art.Name, \"src\": src, \"dst\": dst}).Debug(ctx, \"linked input artifact\")\n\t}\n\treturn nil\n}\n\n// waitForSupervisorReady blocks until the supervisor's status marker reports a\n// terminal outcome (READY/FAILED), or until the supervisor is presumed dead.\n// Used only in init-less pod mode where main and supervisor start concurrently.\n// VarRunArgoPath itself is guaranteed to exist because the emissary has\n// already created /var/run/argo/ctr/<name> earlier in main, which MkdirAll'd","sourceCodeStart":413,"sourceCodeEnd":449,"githubUrl":"https://github.com/argoproj/argo-workflows/blob/35bff19146f5a6ada77468c431f2624bd577e373/cmd/argoexec/commands/emissary.go#L413-L449","documentation":"A companion guard to the overlap check: when staging an input artifact that must overwrite an existing path, argoexec refuses if the resolved path *contains* a nested volume mount — deleting the path would recurse into and destroy a mounted volume declared lower in the tree. The executor fails fast to protect the volume's data.","triggerScenarios":"art.Path (after parent symlink resolution) is a parent directory of a declared volumeMount, the path already exists, and the staging code would RemoveAll it.","commonSituations":"Declaring an input artifact at /data while another mount is at /data/cache; artifact path at a directory root that a subPath volumeMount hangs off; broad artifact paths like / used as destinations.","solutions":["Narrow the input artifact's `path` to a specific file, not a directory that contains a mount point","Move the nested volumeMount outside the artifact's path subtree (change mountPath)","Reorder: don't declare the artifact path as an ancestor of any mountPath","Read the resolved path in the error message and verify with `kubectl describe pod` which mountPath falls under it"],"exampleFix":"# before\ninputs:\n  artifacts:\n  - name: bundle\n    path: /work                      # /work/cache is a mounted PVC\nvolumeMounts:\n- name: cache\n  mountPath: /work/cache\n# after\ninputs:\n  artifacts:\n  - name: bundle\n    path: /tmp/bundle\nvolumeMounts:\n- name: cache\n  mountPath: /work/cache","handlingStrategy":"validation","validationCode":"// Ensure no volumeMount's mountPath is nested under the artifact destination path:\nconst containsMount = (artPath, mounts) =>\n  mounts.some(m => m.mountPath === artPath || m.mountPath.startsWith(artPath === '/' ? '/' : artPath + '/'));\nif (containsMount(art.path, template.volumeMounts || []))\n  throw new Error(`artifact path ${art.path} contains a mountPath; narrow the path`);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Point input artifacts at specific files, never at directory roots that host mounts","Audit templates with both artifacts and volumeMounts for ancestor/descendant path relationships","Keep mountPoints shallow and artifact paths deep and specific","Run `argo lint` plus a local path-overlap check in CI for workflow templates"],"tags":["kubernetes","argo-workflows","artifacts","volume-mount","data-safety"],"backgroundTag":"artifact-path-overlaps-volume-mount","analyzedSha":"35bff19146f5a6ada77468c431f2624bd577e373","analyzedAt":"2026-09-03T19:34:35.908Z","contentChangedAt":"2026-09-03T19:34:35.908Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}