FAQ & Troubleshooting¶
General¶
Do I have to use SecurityContext? Can I just use the tools as-is?¶
Yes — pass nothing (ctx=None, or just don't pass ctx at all) and every
tool behaves like a normal, unrestricted LangChain tool. Nothing in this
package is forced on you; the security layer only activates when you
explicitly configure it. See Quickstart.
Why does make_process_tool() print a warning?¶
Two separate warnings can appear:
- Building it with no
SecurityContextat all warns because that configuration means "run any command, no limits whatsoever" — worth an explicit heads-up even though it's allowed. - Building it with
shell=True(the default) warns that command chaining via shell metacharacters (;,|,&&) isn't individually validated againstallowed_commands/denied_commands— only the outer command string is checked.
Neither warning stops the tool from working; they're informational. See
Process Tool for the full
tradeoff and recommended mitigation (shell=False + allowlist mode).
What happens if I don't set paths?¶
An empty paths list means "no restriction on location" — the context
still enforces permissions, require_approval_for, and (for
process/rsync) command rules, but any path anywhere on the filesystem is
otherwise reachable. If you want a true sandbox, set paths explicitly
with at least one PathEntry (typically slug="root" for a single
directory). See Security & Sandboxing.
Approval¶
CLIApproval isn't prompting me / raises EOFError¶
CLIApproval uses a blocking input() call, which requires an
interactive terminal with a real stdin. If you're running non-interactively
(a script piped from a file, a CI job, a background process), there's
nothing to prompt — by default it auto-denies on EOFError
(auto_deny_on_eof=True). For non-interactive environments, use
InterruptApproval (LangGraph),
CallbackApproval (wrap your
own logic), or AutoApprove()/AutoDeny() if you specifically want a
non-interactive fixed decision.
require_approval_for is set but nothing seems to be blocking — why did the action still go through?¶
Double check the flag actually overlaps: require_approval_for only gates
the specific Permission flags you list. require_approval_for=Permission.
DELETE won't gate WRITE actions. Also confirm approval isn't None —
if it is, the action should be denied, not silently allowed (fail-safe
default); if you're instead seeing it silently succeed, check whether the
permission you expected to be gated is actually the one the operation you
called maps to (see each tool's operation-to-permission table).
ImportError: InterruptApproval requires langgraph¶
Install the extra: pip install -e ".[langgraph]". See
Installation.
Search & Rsync¶
Do I need to install ripgrep or rsync?¶
No — both are optional. file_search_tool falls back to grep, then a
pure-Python implementation; rsync_tool falls back to a Python copy-based
sync. Installing the binaries just makes those tools faster; behavior is
otherwise equivalent. See
Installation.
My search results seem incomplete / capped¶
max_results defaults to 100 and is hard-capped at 1000 regardless of what
you request — this is intentional, since results are meant to be read by
an LLM in one response. Narrow your query/glob/path instead of trying
to raise the cap further, or run multiple, more targeted searches. See
Search Tool.
Commands (process_tool / rsync_tool)¶
My allowlist glob isn't matching what I expect¶
Glob patterns (anything containing * ? [ ]) are matched against the
whole command string, case-insensitively — not as a substring search.
"git *" matches "git status" but not "my git status" (leading text
before "git" means it doesn't match the whole-string shape). If you need
"contains this substring/construct anywhere," use an explicit re:
pattern instead, e.g. r"re:\bgit\b". See
Security & Sandboxing
for the reasoning behind this being explicit rather than guessed.
A command I expect to be blocked (or allowed) isn't behaving as expected¶
Remember the check order: denied_commands is checked first and
always wins, even over an explicit allowed_commands entry. If
allowed_commands is non-empty, it's allowlist mode — anything not
matching something in it is denied by default, regardless of whether
denied_commands says anything about it at all.
Why is bash -c '...' blocked even though I didn't ask for that?¶
It's in the default denied_commands list. The pattern matcher can only
ever see the outer command string — it has no way to safely parse what's
inside an opaque sub-shell string like bash -c 'rm -rf /', so none of the
other denylist rules could catch a dangerous command hidden that way. The
wrapper itself is blocked as the only way to guarantee the rest of the
denylist can't be trivially bypassed. See
Security & Sandboxing.
Can I fully trust the command denylist to keep an agent safe?¶
Treat it as best-effort, not a hard guarantee — it's string matching, not
a real shell parser. For anything with real filesystem access, prefer
allowed_commands (allowlist, default-deny) over relying on
denied_commands alone, and pair it with process_tool's shell=False
mode, which removes the shell-interpretation layer entirely rather than
trying to filter around it. See
Process Tool.
Errors¶
What's the difference between a "Denied: ..." and an "Error: ..." response?¶
"Denied: ..." means the security layer (a MeroToolsError subclass)
stopped the action — a path/permission/command/approval check failed.
"Error: ..." means the action was allowed but failed for an ordinary
reason (missing file, missing required argument, unknown operation). See
Error Handling.
A tool call raised a real Python exception instead of returning a string — is that a bug?¶
Only MeroToolsError and its subclasses are caught and converted to
strings. Any other exception (a genuine bug, an unexpected OS error, etc.)
propagates normally — this is intentional, so real bugs stay visible
rather than being silently reported to the agent as a permission issue. If
you hit an unexpected raised exception that you believe should have been
handled cleanly, that's worth reporting as a bug in the specific tool.
Still stuck?¶
- Re-check Core Concepts for how
guard()ties everything together — most "unexpected behavior" questions trace back to a permission/path/approval interaction covered there. - Check the specific tool's own doc page for operation-level detail: File, Directory, Search, Process, Rsync.
- The test suite (
tests/) is a good source of concrete, working examples for every feature described in these docs.