larksuite/cli · error

cannot resolve symlinks: %w

Error message

cannot resolve symlinks: %w

What it means

SafeEnvDirPath resolves symlinks through the nearest existing ancestor of the directory. If Lstat/EvalSymlinks on that ancestor chain fails (permission denied on a component, I/O error, or a symlink loop the resolver cannot evaluate), the function fails closed and wraps the OS error with this message.

Source

Thrown at internal/vfs/localfileio/path.go:137

	return value, nil
}

// SafeEnvDirPath validates an environment-provided application directory path.
// It requires an absolute path, rejects control characters, normalizes the
// input, and resolves symlinks through the nearest existing ancestor.
func SafeEnvDirPath(path, envName string) (string, error) {
	if err := charcheck.RejectControlChars(path, envName); err != nil {
		return "", err
	}

	path = filepath.Clean(path)
	if !filepath.IsAbs(path) {
		return "", fmt.Errorf("%s must be an absolute path, got %q", envName, path)
	}

	resolved, err := resolveNearestAncestor(path)
	if err != nil {
		return "", fmt.Errorf("cannot resolve symlinks: %w", err)
	}
	return resolved, nil
}

// safePath is the shared implementation for SafeOutputPath and SafeInputPath.
// A path is accepted when its real location falls inside the built-in
// allowlist (cwd, /tmp, ~/files) and outside the built-in denylist; deny wins
// over allow, cwd included. Both lists are compiled in (policy.go), which
// also documents the two bounded environment inputs that remain.
func safePath(raw, flagName string) (string, error) {
	isOutputFlag := flagName == "--output"
	if err := charcheck.RejectControlChars(raw, flagName); err != nil {
		return "", err
	}
	if strings.TrimSpace(raw) == "" {
		return "", fmt.Errorf("%s must not be empty", flagName)
	}
	if err := validatePathPlatform(raw); err != nil {

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Check permissions on each path component: `namei -l /the/path` and fix with chmod/chown
  2. Inspect the chain for symlink loops: `ls -la` each component and remove the offending link
  3. Verify the mount is active (mount / df) if the path lives on a network or automounted volume
  4. Point the env var at a simpler path without symlinks

Example fix

# before
export LARK_APP_DIR=/mnt/nfs/app   # NFS server down
# error: cannot resolve symlinks: lstat /mnt/nfs: no such file or directory
# after (mount recovered) or:
export LARK_APP_DIR=/opt/local/app
Defensive patterns

Strategy: fallback

Validate before calling

if _, err := filepath.EvalSymlinks(existingPrefix(v)); err != nil {
    return fmt.Errorf("cannot resolve %q: %w", v, err)
}

Try / catch

dir, err := localfileio.SafeEnvDirPath(os.Getenv("MY_APP_DIR"), "MY_APP_DIR")
if err != nil && strings.Contains(err.Error(), "cannot resolve symlinks") {
    dir = fallbackDir // e.g. "/opt/myapp"
}

Prevention

When it happens

Trigger: Calling SafeEnvDirPath where resolveNearestAncestor hits an unreadable parent directory (EACCES), a symlink cycle, or a stale automount/NFS mount point on the path.

Common situations: Env var pointing into another user's home with no search permission; a broken symlink loop in the ancestor chain; network mounts that dropped; paths under /media or automounted dirs that are currently offline.

Related errors


AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/0053d5b33486e5ca. Report an issue: GitHub.