argoproj/argo-workflows · error

failed to symlink input artifact %q (%s -> %s): %w

Error message

failed to symlink input artifact %q (%s -> %s): %w

What it means

After clearing (or confirming absence of) the destination, argoexec creates the symlink src -> dst for the input artifact. If os.Symlink fails, this wrapped error is returned. It usually wraps EEXIST (a path component raced or the clear was skipped) or permission/errno errors on the parent directory.

Source

Thrown at cmd/argoexec/commands/emissary.go:438

			// 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.
// Used only in init-less pod mode where main and supervisor start concurrently.
// VarRunArgoPath itself is guaranteed to exist because the emissary has
// already created /var/run/argo/ctr/<name> earlier in main, which MkdirAll'd
// the full parent chain.
func waitForSupervisorReady(ctx context.Context) error {
	return waitForSupervisorReadyAt(ctx, common.StatusMarkerPath, supervisorHeartbeatTimeout, supervisorStatusPollInterval)
}

// waitForSupervisorReadyAt is the parameterized form used by tests; production
// calls waitForSupervisorReady with the constants.

View on GitHub (pinned to 35bff19146)

Solutions

  1. Check the wrapped errno in the full error for the precise cause (EEXIST vs EACCES vs EPERM)
  2. Ensure the parent directory of `path` is writable by the container user or run with a securityContext granting write access
  3. Pick a destination on a symlink-capable filesystem (emptyDir, overlayfs)
  4. Avoid concurrent writers to the same artifact path within the container
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure destination parent is writable and filesystem supports symlinks:
# in an init/debug step:
test -w "$(dirname /tmp/app/config.yaml)" || echo "parent not writable"
mkdir -p /tmp/app && ln -s /tmp/probe /tmp/app/.probe 2>&1 || echo "symlinks unsupported"

Try / catch

err := linkInputArtifacts(ctx, tmpl)
if err != nil {
    var perr *os.LinkError
    if errors.As(err, &perr) {
        log.Printf("symlink failed op=%q old=%q new=%q err=%v", perr.Op, perr.Old, perr.New, perr.Err)
        // EACCES -> fix permissions; EEXIST -> concurrent writer; EPERM -> fs lacks symlink support
    }
    return err
}

Prevention

When it happens

Trigger: os.Symlink(src, dst) errors during linkInputArtifactsAt: dst reappeared between RemoveAll and Symlink, the parent is not writable, or dst sits on a filesystem that does not support symlinks (e.g. some network filesystems).

Common situations: Non-root container writing to a root-owned directory; FAT/exFAT or certain fuse/network mounts lacking symlink support; concurrent processes recreating the destination path; SELinux/AppArmor denial.

Related errors


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