kataras/iris · error

dest directory: %s: eval symlinks: %w

Error message

dest directory: %s: eval symlinks: %w

What it means

Returned by the multipart save/upload helpers when filepath.EvalSymlinks fails to resolve the canonical path of the destination directory (prefixDir). EvalSymlinks errors if any path component does not exist or is an unresolvable symlink, meaning the configured upload destination is broken or absent. The library fails closed rather than writing to an unverified location.

Source

Thrown at context/context.go:2429

	if !isValidFilename {
		// Reject the input as it is invalid or unsafe.
		return prefixDir, name, false, nil
	}

	if ValidExtensionRegexp != nil && !ValidExtensionRegexp.MatchString(filename) {
		// Reject the input as it is invalid or unsafe.
		return prefixDir, name, false, nil
	}

	var destPath string
	if prefixDir != "" {
		// Join the sanitized input with the destination directory.
		destPath = filepath.Join(prefixDir, filename)

		// Get the canonical path of the destination directory.
		canonicalDestDir, err := filepath.EvalSymlinks(prefixDir) // the prefix dir should exists.
		if err != nil {
			return prefixDir, name, false, fmt.Errorf("dest directory: %s: eval symlinks: %w", prefixDir, err)
		}

		// Check if the destination path is within the destination directory.
		if !strings.HasPrefix(destPath, canonicalDestDir) {
			// Reject the input as it is a path traversal attempt.
			return prefixDir, name, false, nil
		}
	}

	return destPath, filename, true, nil
}

// UploadFormFiles uploads any received file(s) from the client
// to the system physical location "destDirectory".
//
// The second optional argument "before" gives caller the chance to
// modify or cancel the *miltipart.FileHeader before saving to the disk,
// it can be used to change a file's name based on the current request,

View on GitHub (pinned to 7bedaf55a0)

Solutions

  1. Create the destination directory with os.MkdirAll(prefixDir, 0o755) before calling the upload API.
  2. Verify the directory exists and all symlinks resolve in your deployment.
  3. Log the wrapped EvalSymlinks error from the message to identify the exact failing component.
  4. Avoid dangling symlinks in the upload root; use real directories in production.

Example fix

// before
dir := cfg.UploadDir
ctx.UploadFormFiles(dir)
// after
dir := cfg.UploadDir
if err := os.MkdirAll(dir, 0o755); err != nil {
    return fmt.Errorf("prepare upload dir: %w", err)
}
ctx.UploadFormFiles(dir)
Defensive patterns

Strategy: validation

Validate before calling

if _, err := os.Stat(uploadDir); err != nil {
    return fmt.Errorf("upload dir unusable: %w", err)
}
if _, err := filepath.EvalSymlinks(uploadDir); err != nil {
    return fmt.Errorf("upload dir symlink broken: %w", err)
}

Type guard

func uploadDirReady(dir string) bool {
    fi, err := os.Stat(dir)
    return err == nil && fi.IsDir()
}

Try / catch

path, name, ok, err := ctx.SaveFormFile(fh)
if err != nil {
    if strings.Contains(err.Error(), "eval symlinks") {
        return fmt.Errorf("upload destination invalid: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: ctx.UploadFormFiles/SaveFormFile with a dest directory that does not exist, was deleted at runtime, contains a dangling symlink, or has a permission error on one of its components.

Common situations: Configured upload path points to a volume not mounted in the container; temp dir cleaned between restarts; symlinked upload roots removed by deploy scripts; permission-restricted directories.

Related errors


AI-assisted analysis of kataras/iris@7bedaf55a0 (2026-08-30). Data as JSON: /api/errors/5d6aa8a7e5184397. Report an issue: GitHub.