larksuite/cli · error
%s %q is outside the built-in allowlist; allowed roots are %
Error message
%s %q is outside the built-in allowlist; allowed roots are %s (hint: save under one of the allowed roots; flags that support stdin can read an out-of-tree file via '-')
What it means
The VFS policy is allowlist-based: local paths must resolve inside one of the built-in allowed roots. checkAllow resolves the input, matches it against the roots (including by file identity for symlink/alias cases), and reports the allowed roots in the error, plus a hint that stdin ('-') can supply out-of-tree data.
Source
Thrown at internal/vfs/localfileio/policy.go:335
// checkAllow accepts paths under any built-in allow root; everything else is
// rejected with the full allowlist spelled out. Only the resolved form of the
// input participates: matching the pre-resolution literal would grant access
// to any symlink placed inside an allow root, no matter where it points.
func checkAllow(flagName, raw, resolved, cwd string) error {
roots := allowRoots(cwd)
for _, e := range roots {
if matchResolved(resolved, e) {
return nil
}
}
// Identity matching also settles the permissive direction: when an
// ancestor is the very same directory as an allow root, the target really
// is inside it, whatever spelling reached it.
if _, ok := matchByFileIdentity(resolved, roots); ok {
return nil
}
return fmt.Errorf("%s %q is outside the built-in allowlist; allowed roots are %s "+
"(hint: save under one of the allowed roots; flags that support stdin can read an out-of-tree file via '-')",
flagName, raw, allowRootsLabel())
}
// matchResolved reports whether the fully resolved input path falls under the
// entry in either of the entry's namespaces. Comparing the input's resolved
// form against the entry literal is safe in both directions: it only matches
// when the real filesystem location truly is under that literal path.
func matchResolved(resolved string, e policyEntry) bool {
return isUnderDir(foldCase(resolved), foldCase(e.resolved)) ||
isUnderDir(foldCase(resolved), foldCase(e.literal))
}
// foldCase normalizes case on platforms whose default filesystems compare
// case-insensitively: NTFS on Windows and APFS/HFS+ on macOS, where
// ~/.SSH/id_rsa and ~/.ssh/id_rsa are the same file and a byte-for-byte
// comparison would walk straight past a deny root. Folding can over-match on
// the rarer case-sensitive volumes of those platforms, which errs towardView on GitHub (pinned to 7fd6ef3c07)
Solutions
- Save/read the file under one of the allowed roots listed in the error message
- For input, use '-' (stdin) if the flag supports it: cat /etc/hosts | lark-cli ... --file -
- Copy or move the file into an allowed root before invoking the command
- Check for symlinks resolving outside the root and reference the real in-root path
Example fix
// before lark-cli im file upload --file /var/tmp/report.pdf // after cp /var/tmp/report.pdf ~/Downloads/ && lark-cli im file upload --file ~/Downloads/report.pdf
Defensive patterns
Strategy: validation
Validate before calling
resolved, _ := filepath.EvalSymlinks(path)
for _, root := range allowedRoots { // from the error message or schema
if strings.HasPrefix(resolved, root) { return true }
}
return false Try / catch
if strings.Contains(err.Error(), "outside the built-in allowlist") {
// parse allowed roots from the message, move the file under one, or pipe via '-'
} Prevention
- Save outputs and stage inputs under one of the documented allowed roots
- Use '-' stdin input for out-of-tree read-only data when the flag supports it
- Verify symlinks resolve inside the root before passing them
When it happens
Trigger: Passing a file path outside every allow root (e.g. /etc/... or an arbitrary user directory) to a file-accepting flag while the policy runs in allowlist mode; also occurs when a symlink resolves outside the roots.
Common situations: Reading an input file from a scratch directory, downloading to a random folder, or CI runners with temp dirs outside the allowed roots.
Related errors
- %s: path must be absolute, got %q
- %s: cannot stat %q: %w
- %s: path %q is a directory, not a file
- %s: path %q is a symlink (not allowed)
- %s: path %q is world-readable (mode %04o)
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/54a57e9516c29703.
Report an issue: GitHub.