AI Agent Guardrails Are Too Aggressive: The False Positive Problem
You added guardrails because the agent shouldn't be able to delete the database. Good instinct. Now the guardrail is blocking a file read in /tmp/agent-cache/results.json. Your operator can't get through a basic task without the guard firing on something harmless. So they disable it. Now you have no guardrail and no audit trail.
This is the false positive spiral. AI agent guardrails false positives are the most common reason teams strip guardrails out of production agent setups — and it's entirely avoidable if you understand why guards fire on safe actions and what to do instead.
I wrote about this as Failure Mode 2 in Why Most AI Agent Setups Fail Within 48 Hours: over-aggressive guardrails that block legitimate actions until operators disable them entirely. This post is the deep dive — what's actually happening inside the guard, what it costs you, and the fix that doesn't require removing the guard.
Why false positives happen: pattern-matching vs. intent-aware guards
Most guardrail implementations are pattern matchers. They look at the surface form of an action — the command string, the file path, the tool name — and decide whether it looks dangerous. This works for the obvious cases. rm -rf / is dangerous. cat /etc/passwd is suspicious. curl evil.com is bad.
The problem is everything else.
A pattern-matching guard sees write /tmp/agent-cache/results.json and panics because the path contains /tmp and the operation is a write. It doesn't know that the agent created that cache directory three seconds ago, that the file is scoped to the current task, and that nothing outside the sandbox can read it. It just sees a write to a temp path and fires.
Here's what I've seen pattern-matching guards block in real deployments (Hermes PR #36231 fixed exactly this class of false positive in the skills guard):
- Reading a cached file from a project-local temp directory (flagged as "suspicious temp access")
- Writing to
./output/inside the agent's own workspace (flagged as "unauthorized write") - Running
git status(flagged as "shell command execution") - Searching for a file by glob pattern (flagged as "directory traversal risk")
- Calling
curl localhost:3000/health(flagged as "untrusted network request")
Every one of those is a legitimate agent action. Every one stopped the workflow. Every one required an operator to intervene, investigate, decide it was safe, and override the guard. After the fifth time in a day, they stop overriding and start disabling.
Intent-aware guards work differently. Instead of matching the surface form, they evaluate the action in context: who initiated it, what task it serves, what the resolved path actually is, whether the scope matches the agent's current authorization, and whether the operation is reversible. An intent-aware guard knows that write /tmp/agent-cache/results.json is the agent writing to its own cache because it checks the resolved path against the agent's workspace boundary, not against a regex.
The difference is not academic. In my experience, pattern-matching guards have false positive rates in the 15-40% range on real agent workloads — meaning roughly one in three legitimate actions gets blocked. Intent-aware guards, when properly configured with scoped allowlists and path resolution, bring that down to low single digits. The false positive rate is the entire ballgame. A guard with 40% false positives is not a guard. It's a speed bump that teaches operators to route around it.
The cost: trust decay, shadow bypass, and unguarded agents
False positives aren't just annoying. They compound into a structural failure that leaves you worse off than if you'd never added guardrails at all.
Trust decay
Every false positive is a small betrayal of the operator's trust. The first time the guard fires on a safe action, the operator investigates. The fifth time, they glance at it and dismiss it. The twentieth time, they stop looking. The guard has cried wolf enough times that when it fires on something genuinely dangerous, nobody is paying attention.
This is the same pattern that killed email alerts, pager fatigue, and every monitoring system that ever generated too many notifications. The signal-to-noise ratio determines whether the guard is useful. A guard with 40% false positives has a signal-to-noise ratio where the noise drowns the signal. Operators don't tune out the false positives — they tune out the guard entirely.
Shadow bypass
When the guard blocks a legitimate action, the operator needs to get work done. So they find a way around it. This looks like:
- Running the agent without the guardrail skill loaded
- Adding the blocked path to an allowlist without reviewing the blast radius
- Executing the blocked action manually, outside the agent's audit trail
- Forking the agent config to a version without the guard and never merging it back
Each of these creates a shadow path. The agent runs, work gets done, but the guardrail is bypassed, not fixed. The bypass isn't logged, isn't reviewed, and isn't version-controlled. It's just an operator who learned that the guard is in the way and quietly removed it.
Unguarded agents
This is where the spiral completes. The guard was too aggressive, the operator bypassed it, the bypass became permanent, and now the agent runs with no guard at all. The team thinks they have guardrails because the guard is still in the config. The operator knows they don't because they disabled it two weeks ago to get through a deadline.
The worst version of this is when the guard is still technically active but has been so diluted — allowlist entries piled on top of allowlist entries, exceptions carved out for every false positive — that it blocks nothing. You've built a guard that exists only to satisfy a checkbox and catches exactly zero dangerous actions.
The fix: intent-aware gating, not hard blocks
The solution isn't better pattern matching. You can't regex your way out of a context problem. The solution is a fundamentally different guard model.
Resolve before you match
Before a guard evaluates an action, it should resolve the action to its concrete form. That means:
- Resolve symlinks and relative paths to absolute paths before checking against an allowlist
- Expand environment variables and globs before evaluating the operation
- Check the resolved network destination, not the hostname string (localhost is not untrusted)
- Verify the operation scope against the agent's current task, not just the tool name
A guard that checks the resolved path knows that write /tmp/agent-cache/results.json is scoped to the agent's workspace. A guard that checks the raw string sees /tmp and panics. Resolution is the difference between a guard that works and a guard that generates support tickets.
Allowlist, not blocklist
I covered this in the parent post, but it bears repeating because it's the single highest-leverage change you can make. Blocklists catch 90% of bad actions and generate 50% false positives on the long tail. Allowlists are more work upfront — you have to define what's permitted — but they scale. You define the blast radius. Everything outside it is blocked, and the false positive rate drops to near zero because the guard is checking against a set you explicitly designed.
The allowlist doesn't have to be exhaustive. Start with the agent's workspace, the tools it's authorized to use, and the endpoints it's allowed to call. Everything else is a question, not a block. For the sandbox boundary checklist we use — files, shell, network, secrets, rollback — see AI Agent Sandbox Checklist.
Escalate, don't block
This is the fix most teams miss. When a guard fires on something it can't classify, the default behavior should not be to block the action. It should be to escalate to a human and let the agent continue with other work.
A hard block stops the workflow. The operator has to intervene, investigate, and override. That's the friction that drives shadow bypass. An escalation says: "I'm not sure about this one. Here's what the agent wants to do, here's why the guard fired, here's the context. Review and approve, and I'll continue."
This is an approval gate, not a guardrail. The distinction matters. A guardrail says no. An approval gate says "not yet." Guardrails are for actions you know are dangerous. Approval gates are for actions you're not sure about. Most false positives fall into the second category — the guard can't tell if it's safe, so it blocks, when the right move is to escalate.
For the full gate taxonomy — publish, spend, delete, send, merge, production — see Human-in-the-Loop AI Agents: Approval Gates That Make Automation Useful. The short version: the agent prepares the action, the guard can't classify it, the system routes it to a human with enough context for a fast decision, and the agent continues with everything else it was doing.
Log guard decisions, not just guard blocks
When a guard fires — whether it blocks or escalates — log the decision. The log entry should contain: the action, the resolved form, the rule that fired, the context that was available, and the outcome. This does two things:
- It gives operators a record of what the guard is doing, so they can identify patterns in false positives and tune the rules.
- It gives you a false positive rate you can measure. If you can't measure it, you can't improve it.
A guard that blocks silently is worse than no guard. At least with no guard you know you're exposed. A silent guard gives you the illusion of safety while the operator has already bypassed it. Log every guard decision. Review the log weekly. If the false positive rate is above 5%, tune the rules. If it's above 20%, the guard model is wrong and you need to move to allowlisting or intent-aware gating. For the observability stack that makes this measurable — task-layer metrics, tool-call traces, context pressure — see Monitoring AI Agents in Production.
The pattern I see in teams that survive
Teams that keep their guardrails past the first week do three things differently:
-
They measure false positive rate. They know exactly how often the guard fires on safe actions, and they treat that number as a health metric. High false positive rate means the guard needs tuning, not that the operator needs to be more patient.
-
They escalate instead of block. Actions the guard can't classify go to a human, not to a hard stop. The agent keeps working on everything else. The operator makes a fast decision with context, and the action either proceeds or stops. No friction, no bypass.
-
They start strict and earn autonomy. Every release action starts gated. The guard fires on everything ambiguous. But they measure, tune, and narrow the gate as the data comes in. After 50 clean approvals on a class of work, they consider auto-approving it. The default is strict. The earned state is autonomy.
Teams that fail do the opposite: they add guards, get flooded with false positives, disable the guards to keep shipping, and end up with agents running unguarded and no audit trail of what the guards used to catch.
This is an operator problem, not a model problem
Guardrail false positives are not an AI problem. The model didn't cause them. A better model won't fix them. They're a systems design problem: the guard is checking the wrong thing (surface pattern instead of resolved intent), responding the wrong way (hard block instead of escalation), and generating the wrong feedback (silent blocks instead of logged decisions).
The fix is boring systems work: path resolution, allowlists, approval gates, structured logs, and metrics on guard decisions. None of it is exciting. All of it is what separates a guardrail that survives production from one that gets disabled in week one.
If your guardrails are blocking legitimate actions, the answer isn't to remove the guardrails. It's to make them smart enough to know the difference.
