larksuite/cli · error
%s: cannot resolve symlink %q: %w
Error message
%s: cannot resolve symlink %q: %w
What it means
AssertSecurePath audits a file path used for secret/command binding. When the target is a symlink and AllowSymlinkPath is true, resolveSymlinkIfAllowed calls vfs.EvalSymlinks to fully resolve it; this error wraps any failure from that resolution (e.g. a dangling link, a permission-denied directory along the chain, or an ELOOP cycle). It is thrown to prevent auditing an unresolvable path.
Source
Thrown at internal/binding/audit.go:106
return nil, fmt.Errorf("%s: path %q is a directory, not a file", label, target)
}
return info, nil
}
// resolveSymlinkIfAllowed resolves a symlink to its target when
// params.AllowSymlinkPath is true, or rejects it otherwise. When the input
// is not a symlink, target is returned unchanged. A symlink that points to
// another symlink is rejected so callers only deal with a single hop.
func resolveSymlinkIfAllowed(target string, linfo fs.FileInfo, params AuditParams) (string, error) {
if linfo.Mode()&os.ModeSymlink == 0 {
return target, nil
}
if !params.AllowSymlinkPath {
return "", fmt.Errorf("%s: path %q is a symlink (not allowed)", params.Label, target)
}
resolved, err := vfs.EvalSymlinks(target)
if err != nil {
return "", fmt.Errorf("%s: cannot resolve symlink %q: %w", params.Label, target, err)
}
rinfo, err := vfs.Lstat(resolved)
if err != nil {
return "", fmt.Errorf("%s: cannot stat resolved path %q: %w", params.Label, resolved, err)
}
if rinfo.Mode()&os.ModeSymlink != 0 {
return "", fmt.Errorf("%s: resolved path %q is still a symlink", params.Label, resolved)
}
return resolved, nil
}
// requireInTrustedDirs enforces that effectivePath lives under one of the
// caller-declared trusted directories, if any were declared. An empty
// trustedDirs list disables the check.
func requireInTrustedDirs(effectivePath string, trustedDirs []string, label string) error {
if len(trustedDirs) == 0 {
return nil
}View on GitHub (pinned to 7fd6ef3c07)
Solutions
- Check the wrapped cause with errors.Unwrap / %w output: if it is 'no such file or directory', recreate the symlink so it points at an existing file
- If the path should be a plain file, replace the symlink with the real file or unset the symlink-related config entry
- Verify every directory component in the symlink chain is readable/executable by the current user (ls -ld each component)
- Ensure the storage backing the symlink target is mounted (NFS/autofs/home volume)
- Keep symlink chain length to a single hop — the audit rejects multi-hop links anyway
Example fix
// before: dangling link ln -s /opt/old-tool/bin/agent /usr/local/bin/agent // after: point at an existing target ln -s /opt/tool/bin/agent /usr/local/bin/agent
Defensive patterns
Strategy: validation
Validate before calling
func precheckSymlink(p string) error {
fi, err := os.Lstat(p)
if err != nil { return err }
if fi.Mode()&os.ModeSymlink == 0 { return nil }
resolved, err := filepath.EvalSymlinks(p)
if err != nil { return fmt.Errorf("symlink %s unresolvable: %w", p, err) }
if _, err := os.Stat(resolved); err != nil { return err }
return nil
} Type guard
func isSymlink(fi fs.FileInfo) bool { return fi.Mode()&os.ModeSymlink != 0 } Prevention
- Run readlink -f on configured paths before saving them into config
- Never leave dangling symlinks in directories referenced by config
- Avoid symlink chains; point directly at the final file
- Keep symlink target directories accessible to the user running the CLI
When it happens
Trigger: Target path is a symlink, params.AllowSymlinkPath is true, and EvalSymlinks fails: broken symlink (target does not exist), too many levels of symlink indirection, or a directory component of the link chain denies search permission to the current user.
Common situations: Config points at /usr/local/bin/foo which is a symlink whose target was uninstalled; symlinks pointing into a root-only directory while the CLI runs unprivileged; home-dir symlinks into unmounted volumes (e.g. dead autofs/NFS mounts); symlink loops after manual relinking.
Related errors
- %s: cannot stat resolved path %q: %w
- %s: resolved path %q is still a symlink
- %s: path %q is a symlink (not allowed)
- %s: cannot stat %q: %w
- cannot resolve symlinks: %w
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/f72c94da236fe067.
Report an issue: GitHub.