larksuite/cli · error
%s: path must be absolute, got %q
Error message
%s: path must be absolute, got %q
What it means
requireAbsolutePath (invoked via AssertSecurePath in the binding security audit) rejects any path that is not absolute under Go's filepath.IsAbs rules. The audit is intentionally stdlib-strict: shortcuts like '~' are home-relative and must be pre-resolved by callers such as resolveFileRef before audit. The label names which audited path failed.
Source
Thrown at internal/binding/audit.go:75
if err := auditFilePermissions(effectivePath, params.AllowReadableByOthers, label); err != nil {
return "", err
}
if err := checkOwnerUID(effectivePath, label); err != nil {
return "", err
}
return effectivePath, nil
}
// 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 whenView on GitHub (pinned to 7fd6ef3c07)
Solutions
- Convert to an absolute path at the call site: filepath.Abs(p) or home-dir expansion for '~' before calling AssertSecurePath.
- For '~', expand to os.UserHomeDir()-based absolute path in the config-loading layer (resolveFileRef), not in the audit.
- Fix the config value itself to be an absolute path if it's user-authored.
- Check the label in the message to identify which of the audited paths (file, target, symlink) is relative.
Example fix
// before err := binding.AssertSecurePath(cfg.Path, params) // after abs, _ := filepath.Abs(expandTilde(cfg.Path)) err := binding.AssertSecurePath(abs, params)
Defensive patterns
Strategy: validation
Validate before calling
func ensureAbs(p string) (string, error) {
if strings.HasPrefix(p, "~") {
home, err := os.UserHomeDir()
if err != nil { return "", err }
p = filepath.Join(home, strings.TrimPrefix(p, "~"))
}
return filepath.Abs(p)
}
abs, err := ensureAbs(cfg.Path)
if err != nil { return err }
err = binding.AssertSecurePath(abs, params) Type guard
func isRelativePathErr(err error) bool {
return err != nil && strings.Contains(err.Error(), "path must be absolute")
} Try / catch
if err := binding.AssertSecurePath(p, params); err != nil {
if isRelativePathErr(err) {
abs, aerr := filepath.Abs(p)
if aerr == nil { return binding.AssertSecurePath(abs, params) }
}
return err
} Prevention
- Always resolve '~' and relative paths in the config-loading layer before audit.
- Run filepath.Abs at the boundary where user input enters the program.
- Store absolute paths in config files when the consumer requires them.
- Read the label in the error to identify which audited path is relative.
When it happens
Trigger: AssertSecurePath is given a relative path (e.g. 'config.yaml', './x', '../x') or a '~'-prefixed path that the caller failed to expand before auditing.
Common situations: User config file contains a relative path that was never resolved against cwd/home; a '~' shortcut passed straight from user-authored config into the audit; programmatic callers building paths by concatenation without filepath.Abs.
Related errors
- %s: path %q is a directory, not a file
- %s: cannot stat %q: %w
- %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/d3634b56cc4dc9ae.
Report an issue: GitHub.