Security

When an Agent Action Is Not in the Policy File

Antonio Evans8 min read

A split-screen diagram showing an AI agent attempting an action not listed in a policy file.
Key points

Key points

  1. 1Every policy file has gaps, and the behavior for an unmatched action is a decision you must make explicit rather than inherit.
  2. 2Three fallbacks cover almost every case: default deny, a manual review queue, and hard fail with an alert.
  3. 3Each fallback needs synthetic tests that prove the unmatched path behaves as intended, not just that matched rules work.
  4. 4Log the fallback decision itself, with the reason, so an auditor can reconstruct why an action was stopped or queued.

Why the policy file always has a gap

An agent policy file enumerates rules for actions you anticipated. It cannot enumerate every action an agent will attempt, because the agent chooses tools and arguments at runtime from whatever context it reads, including untrusted pages, tickets and emails. OWASP's AI agent guidance treats all external data as untrusted and warns that agents can be hijacked into unintended tool calls through context they consume the OWASP AI Agent Security Cheat Sheet. When the model proposes something no rule covers, the orchestrator still has to answer one question: does this call execute?

The quiet danger is that most policy engines treat "no matching rule" as allow. The Vercel AI SDK's policy package states this plainly: no matching rule normalizes to a status the SDK treats as allow, and you add an explicit default decision if you want to deny instead the AI SDK policy tool approvals guide. That default is fine for read-only tools and dangerous the moment the agent can write. A prompt instruction saying "never do X" is not an enforcement boundary, because the orchestration layer, not the model, decides whether a call reaches execution NHI Mgmt Group analysis of default-deny controls.

So the coverage gap is not a bug you close once. It is a permanent condition of running agents, and the fallback is a design decision with security consequences.

Three fallbacks, and what each one buys you

The three options differ on two axes: what happens to the action, and what happens to the agent's run.

Default deny blocks the action and returns a denial the model can reason about. The run continues, the agent sees a refusal, and no human is needed. This is the cheapest fallback and the right one for irreversible or externally visible actions. OWASP recommends failing closed for unknown tools or missing approval requirements, meaning the absence of a rule resolves to denial the OWASP AI Agent Security Cheat Sheet.

Manual review queue pauses the action and routes it to a human, who approves, rejects or modifies it. The agent's run suspends and resumes from a serialized state. This is the right fallback when the action is plausibly legitimate but consequential. The hard part is that the state can change while the workflow waits, so a credential valid at pause time is not guaranteed to be valid at resume time, a failure most approval-gate implementations skip Scalekit on human-in-the-loop tool calling.

Hard fail with an alert stops the run entirely and pages someone. This is the fallback for unmatched actions in high-risk tool categories, where the fact that something is unmatched is itself a signal worth investigating. It trades availability for visibility.

FallbackAgent runHuman neededBest forMain risk
Default denyContinues, sees denialNoIrreversible or external actionsAgent loops retrying, wasted tokens
Review queuePauses, resumes laterYes, within a windowHigh-impact writes needing contextApproval fatigue, stale state on resume
Hard fail + alertStopsYes, immediatelySensitive tool classes, policy faultsAvailability loss, alert noise

A useful decision rule: choose by blast radius first, frequency second. If the worst version of the action cannot be undone, default deny. If it can be undone but matters, queue it. If the tool class is one where any unmatched call is suspicious (credential changes, IAM, shell execution), hard fail.

Tiering actions so the fallback fits

The fallback belongs per action class, not per agent. A four-tier risk model is a common way to sort: read-only actions run unattended, reversible low-impact actions notify only, reversible production changes need one approval, and destructive or irreversible actions need two approvals including the service owner Vibranium Labs on production access for agents. The same logic applies to unmatched actions: find the tier the unmatched action most plausibly belongs to, and apply that tier's fallback.

You can infer the tier from tool metadata when the policy has no rule. A practical mapping:

  • Tool declares read-only scope: default allow with logging is defensible, because reads are bounded by what the agent does with the data Scalekit.
  • Tool writes to one record, reversible: queue for review.
  • Tool deletes, moves data, sends external communications, spends money or changes permissions: default deny, and alert if it recurs.

OWASP's list of high-impact actions lines up with the last bucket: sending external communications, deleting or moving important data, executing scripts, making purchases, changing security configurations and using privileged credentials all belong behind a human checkpoint ThreatLocker on human-in-the-loop approvals.

Making the default explicit in code

Whatever engine you use, the fix starts with one line that turns an unstated default into a stated one. In Open Policy Agent's Rego language, the AI SDK documentation shows the pattern: unmatched calls fall through to a "not applicable" decision, which the SDK treats as allow; replacing that default with a deny decision flips the whole posture the AI SDK policy tool approvals guide.

package agent.call

# Every unmatched call resolves here. Choose deliberately.
default decision := { "decision": "deny", "reason": "no policy coverage" }

# Read-only tools stay open even without a specific rule.
decision := { "decision": "allow" } {
  input.tool.scope == "read_only"
}

# Unmatched calls on sensitive tool classes hard fail.
decision := { "decision": "deny", "reason": "unmatched sensitive tool", "escalate": true } {
  input.tool.class == "privileged"
}

The reason field matters more than it looks. It is surfaced back to the model on denial and to the approver on a review request the AI SDK policy tool approvals guide. A model that knows why it was refused can choose a different path instead of retrying the same call in a loop, which is also how agents run up costs.

If you run several model providers behind one orchestrator, keeping the policy at the dispatch layer rather than in provider-specific code means the default applies uniformly. Quanta Inference exposes text, image, video, audio and embedding models through one OpenAI-compatible API key, which keeps that dispatch layer in one place Quanta Inference.

Designing the review queue so it does not become a rubber stamp

A queue that fires too often trains reviewers to click approve without reading, which is worse than no gate at all because it creates the appearance of oversight without the substance Arthur AI on human-in-the-loop governance. Two design choices keep a fallback queue meaningful.

First, decide the timeout behavior before launch. When no approver responds within the window, the action should deny by default, not silently proceed; an approval that times out into execution is a gate that does not exist Arthur AI. Second, give the reviewer enough context to judge the specific call: agent identity, requesting user, tool, target resource, data involved and expected result, all scoped to that one action and time window ThreatLocker.

Tiered approval depth is not exotic. Oracle's project management workflows route budget approvals through one, two or three management levels depending on the amount, with thresholds at 5,000 and 20,000 Oracle's multilevel approval example. Pricefx runs the same idea for pricing: deals above target margin approve automatically, deals between target and floor trigger the workflow, and deals below the floor are rejected or escalated the Pricefx approval workflow handbook. An agent fallback queue should borrow the shape: trivial unmatched actions auto-deny with a log line, moderate ones queue at one level, and sensitive ones queue at two.

GitLab's merge request approval policies illustrate the fail-closed principle at the pipeline level: if the rules are invalid or cannot be evaluated, approval is required by default, and you can change that behavior only through an explicit fallback setting GitLab merge request approval policies. That is the same move you are making for unmatched agent actions.

Testing each fallback path

Matched rules get tested because they are easy to test. Fallbacks get skipped because nobody writes a test for the absence of a rule. The fix is a fixture set of synthetic unmatched actions, one per tool class, each asserting the expected outcome.

A compact test matrix:

Test caseSynthetic actionExpected outcomeWhat you are proving
Unknown read toolNew read-only tool, no ruleAllow, logged with "no coverage, read-only"Default does not block low-risk discovery
Unknown write toolPATCH to an unlisted resourceQueued for review, run pausedQueue path actually pauses, not just logs
Unknown privileged toolShell or IAM call, no ruleDenied, run stopped, alert firedHard fail path reaches the pager
Policy engine downSimulated evaluation errorDenied or queued, never allowedFail-closed under infrastructure fault
Stale approvalApprove, wait past credential expiry, resumeNew decision requestedResume revalidates state

Two of these catch failures that production will otherwise surface for you. The engine-down case mirrors GitLab's "cannot be evaluated means approval required" behavior GitLab. The stale-approval case catches the resume-time gap where a credential expires while the workflow waits Scalekit.

A worked example shows why the assertions need to be specific. Suppose a support agent has five rules covering refunds under 100 dollars, order lookups, ticket updates, email drafts and knowledge base reads. It now attempts to update a shipping address on an order, a tool with no rule. Under default deny, the run continues, the model sees "no policy coverage" and likely asks the customer to contact support, which is acceptable but may strand a legitimate task. Under a queue, the run pauses, a reviewer approves the specific address change in two minutes and the customer stays happy. Under hard fail, the address change is blocked and someone investigates why the policy missed a common request. The right answer depends on frequency: if the same unmatched action appears more than a handful of times in a week, the gap is a missing rule, and the alert path is doing its job by telling you so.

Instrumenting the decision for audit

The fallback decision is itself an event worth logging, separate from the action. A defensible audit trail records an immutable event ID, the principal, the target, a precise timestamp, the agent and model versions, the tool call with structured arguments, the policy outcome, and a chain hash linking the record to the previous one Digital Applied on agent audit trail design. For an unmatched action, the policy outcome field should say something like "no coverage, default deny," not just "denied," so a later reader can tell a rule-based denial from a gap-based one.

Human decisions need their own fields: reviewer identity, review timestamp, the risk flag that prompted the review, and the outcome Runlayer on audit trail best practices. Cluster policy denials as an alert signal, because a spike in denials usually means something is probing for what it is allowed to do Runlayer.

The one metric that tells you whether the fallback is working is coverage rate: unmatched actions divided by total actions, tracked weekly by tool class. It should fall as rules are added, and a flat line means nobody is closing gaps. Pair it with approval latency and the share of approvals that are modified or rejected, which is the best available proxy for whether reviewers are actually reading.

If you are setting this up across several teams, a shared policy library and per-person keys keep the default consistent. Quanta for Teams gives one shared credit pool, a private skill library, admin controls and an API key per person Quanta for Teams.

FAQ

FAQ

What does "default deny" mean for an AI agent?

Default deny means a proposed action is blocked unless a rule or a human explicitly allows it, so the absence of policy coverage resolves to denial rather than permission. Many policy engines treat no matching rule as allow, so you usually have to state the deny default yourself the AI SDK policy tool approvals guide.

Should unmatched actions go to a human or fail outright?

Route to a human when the action is plausibly legitimate and reversible, and hard fail when the tool class is sensitive enough that any unmatched call is suspicious, such as credential or permission changes. The decision rule is blast radius first, then frequency: recurring unmatched actions usually mean a missing rule, not a broken fallback.

How do I test a fallback path that only fires when no rule matches?

Write synthetic tool calls that deliberately target resources and tools with no rule, one per tool class, and assert the expected outcome: allow with a log line, queued and paused, or denied with an alert. Add two infrastructure cases: the policy engine failing, which must never resolve to allow, and an approval that goes stale before resume.

What happens if a reviewer never responds?

The action should deny by default when the approval window expires. An approval that times out into execution is a gate that does not exist, and it removes the accountability the gate was meant to create Arthur AI on human-in-the-loop governance.

What should the log record for an unmatched action?

Record the standard audit fields plus a policy outcome that names the gap, for example "no coverage, default deny," and for review paths the reviewer identity, decision timestamp and outcome. That lets an auditor distinguish a rule-based denial from a coverage gap without asking an engineer.

Sources

Sources

  1. AI Agent Security - OWASP Cheat Sheet Series
  2. Policy-Based Tool Approvals
  3. Default-deny controls for AI agents: why runtime ...
  4. Human-in-the-Loop Tool Calling: Approval Gates for AI ...
  5. should an AI agent have production access?
  6. What should require human approval when using AI agents?
  7. Human-in-the-Loop Governance for AI Agents - Arthur AI
  8. Using the Supervisory List Builder for Multilevel Approval ...
  9. Approval Workflow - Handbook
  10. Merge request approval policies
  11. Agent Audit Trail Design: 7 Best Practices for 2026
  12. AI agent audit trails: audit log best practices
About the author

About the author

Antonio Evans
Antonio Evans

Antonio Evans is the founder of Quanta Intelligence and a fractional CTO who builds HIPAA-ready AI systems for health and life-science companies. Before that he was technical co-founder of FriendsEAT for twenty years, CEO of Socialdraft, and a data engineer at IBM. Antonio lives with his dog Santino in Lisbon, Portugal.

All articles