larksuite/cli · error
%s: cannot stat %q: %w
Error message
%s: cannot stat %q: %w
What it means
lstatNonDir failed to Lstat the audited path via vfs.Lstat and wraps the underlying error with %w (unwrappable). The stat itself failed — most commonly the file does not exist — so the secure-path audit cannot proceed.
Source
Thrown at internal/binding/audit.go:85
// requireAbsolutePath rejects relative paths; relative paths would depend on
// the process cwd and defeat the point of a static audit. Shell-style
// shortcuts like `~` are home-relative, not cwd-relative — they are an
// orthogonal concern and the audit is intentionally Go-stdlib strict here.
// Callers that accept user-authored config (e.g. resolveFileRef) must
// pre-resolve any such shortcuts before passing the path in.
func requireAbsolutePath(target, label string) error {
if !filepath.IsAbs(target) {
return fmt.Errorf("%s: path must be absolute, got %q", label, target)
}
return nil
}
// lstatNonDir stats the path without following symlinks, rejecting
// directories. Returns the stat info for downstream steps to reuse.
func lstatNonDir(target, label string) (fs.FileInfo, error) {
info, err := vfs.Lstat(target)
if err != nil {
return nil, fmt.Errorf("%s: cannot stat %q: %w", label, target, err)
}
if info.IsDir() {
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)
}View on GitHub (pinned to 7fd6ef3c07)
Solutions
- Check the wrapped cause after 'cannot stat': ENOENT means fix or create the path; EACCES means fix directory permissions.
- Verify the configured path exists on the machine running the CLI (ls the exact path).
- Fix typos or stale references in the binding/config that point to moved files.
- If running in a container/sandbox, mount or copy the file into the expected location.
Example fix
// caller
if _, err := os.Lstat(p); err != nil {
return fmt.Errorf("configured file missing, create or fix path %q", p)
}
err := binding.AssertSecurePath(p, params) Defensive patterns
Strategy: validation
Validate before calling
// preflight before AssertSecurePath
if _, err := os.Lstat(p); err != nil {
if os.IsNotExist(err) { return fmt.Errorf("file %q does not exist; create it or fix the config", p) }
return fmt.Errorf("cannot access %q: %w", p, err)
} Type guard
func isStatErr(err error) bool {
return err != nil && strings.Contains(err.Error(), "cannot stat")
} Try / catch
if err := binding.AssertSecurePath(p, params); err != nil {
if isStatErr(err) && errors.Is(err, fs.ErrNotExist) {
return fmt.Errorf("configure an existing file path; %w", err)
}
return err
} Prevention
- Check file existence (and parent-dir readability) before configuring a binding.
- Use vfs/os.Lstat in preflight to match the audit's own check.
- In containers, verify the file is mounted at the expected path.
- Fix permissions (chmod/chown) when the wrapped cause is EACCES.
When it happens
Trigger: AssertSecurePath -> lstatNonDir where vfs.Lstat(target) errors: nonexistent file, permission denied on a parent directory, broken path component, or an invalid path on the host.
Common situations: Binding points at a file that was deleted or renamed; typo in a configured path; running on a host where the mounted workspace lacks the file; parent directory unreadable due to permissions or sandboxing.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
- %s: path must be absolute, got %q
- %s: path %q is a directory, not a file
- %s: path %q is a symlink (not allowed)
- unsafe output path: %w
- %s %q has multiple hard links, so writing it would also rewr
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/db97d26479b4b993.
Report an issue: GitHub.