argoproj/argo-workflows · error
refusing to stage input artifact %q at %s: it resolves to %s
Error message
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
What it means
When staging an input artifact that must overwrite an existing file, argoexec resolves the real filesystem location of art.Path and refuses if it lands inside a user-declared volume mount. Clearing the path there (os.RemoveAll) would destroy the contents of a live PVC/hostPath/emptyDir, so the executor fails fast instead of deleting user data.
Source
Thrown at cmd/argoexec/commands/emissary.go:428
}
} else {
// Something is already at art.Path. Replacing it (os.RemoveAll then
// symlink) reproduces the legacy SubPath mount's shadowing — but only
// when it is safe. RemoveAll resolves symlinks in the parent chain, so
// resolve the parent to find where the delete would actually land
// (resolve the parent, not the final element, so an image symlink *at*
// art.Path is just unlinked rather than followed). If that resolved
// path overlaps a user-declared volume, clearing it would recurse into
// and destroy a live PVC/hostPath/emptyDir, so refuse. Benign system
// mounts (tmpfs /run, the overlay rootfs) are not declared user volumes
// and so remain safe to shadow.
realParent, evalErr := filepath.EvalSymlinks(filepath.Dir(dst))
if evalErr != nil {
return fmt.Errorf("failed to resolve parent of artifact path %q at %s: %w", art.Name, dst, evalErr)
}
resolved := filepath.Join(realParent, filepath.Base(dst))
if mnt := common.FindOverlappingVolume(tmpl, resolved); mnt != nil {
return 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)
}
if mnt := common.FindVolumeMountNestedUnderPath(tmpl, resolved); mnt != nil {
return 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)
}
if rmErr := os.RemoveAll(dst); rmErr != nil {
return fmt.Errorf("failed to clear existing path for artifact %q at %s: %w", art.Name, dst, rmErr)
}
}
if err := os.Symlink(src, dst); err != nil {
return fmt.Errorf("failed to symlink input artifact %q (%s -> %s): %w", art.Name, dst, src, err)
}
logger.WithFields(logging.Fields{"name": art.Name, "src": src, "dst": dst}).Debug(ctx, "linked input artifact")
}
return nil
}
// waitForSupervisorReady blocks until the supervisor's status marker reports a
// terminal outcome (READY/FAILED), or until the supervisor is presumed dead.View on GitHub (pinned to 35bff19146)
Solutions
- Change the input artifact's `path` to a location outside any declared volumeMount (e.g. /tmp/cfg)
- If you need the artifact in the volume, mount the volume at a subdirectory and place the artifact path outside it, or stage the artifact to a temp path and copy it in a script step
- Split into two mounts: one small emptyDir for the input artifact, one for the volume that must stay intact
- Check `argo get <wf>` / executor logs for the resolved path in the message and adjust either path or volumeMount so they do not overlap
Example fix
# before
containers:
- volumeMounts:
- name: work
mountPath: /mnt/work
inputs:
artifacts:
- name: data
path: /mnt/work/data # overwrite would clear the mounted volume
# after
inputs:
artifacts:
- name: data
path: /tmp/inputs/data
script:
command: [sh]
source: cp /tmp/inputs/data /mnt/work/data Defensive patterns
Strategy: validation
Validate before calling
// Before submit, ensure no input artifact path falls inside a template volumeMount:
const overlaps = (artPath, mounts) =>
mounts.some(m => artPath === m.mountPath || artPath.startsWith(m.mountPath + '/'));
if (overlaps(art.path, template.volumeMounts || []))
throw new Error(`artifact path ${art.path} overlaps a volumeMount; move it outside`); Prevention
- Never point input artifact paths at volumeMount directories that hold other data
- Use separate mounts (or a copy step) to combine input artifacts with volume data
- Keep artifact destinations under a dedicated prefix like /tmp/argo-inputs
- Read the resolved path in the error message — it tells you exactly which mountPath conflicts
When it happens
Trigger: An input artifact's `path` resolves (after symlink evaluation of the parent) inside a volumeMount declared on the template, AND something already exists at that path so an overwrite would be required.
Common situations: Pointing an artifact at a path under an output-artifact volume mount (e.g. /mnt/out shared between input and output artifacts mounted via PVC); reusing the same path for an input artifact and a mounted config; mounting a volume at / and putting the artifact path under it.
Related errors
- refusing to stage input artifact %q at %s: it resolves to %s
- failed to re-enter working directory %q after staging input
- failed to stat input artifact %q at %s: %w
- failed to create parent directory for artifact %q at %s: %w
- failed to stat artifact path %q at %s: %w
AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03).
Data as JSON: /api/errors/49d952dd8f570235.
Report an issue: GitHub.