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

  1. Change the input artifact's `path` to a location outside any declared volumeMount (e.g. /tmp/cfg)
  2. 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
  3. Split into two mounts: one small emptyDir for the input artifact, one for the volume that must stay intact
  4. 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

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


AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03). Data as JSON: /api/errors/49d952dd8f570235. Report an issue: GitHub.