Anti-Pattern Agents Without Guardrails: agents without guardrails

Anti-pattern where an agent runs without policy boundaries and system limits.
On this page
  1. Idea In 30 Seconds
  2. Anti-Pattern Example
  3. Why It Happens And What Goes Wrong
  4. Correct Approach
  5. Quick Test
  6. How It Differs From Other Anti-Patterns
  7. Write Access by Default vs Agents Without Guardrails
  8. Blind Tool Trust vs Agents Without Guardrails
  9. No Stop Conditions vs Agents Without Guardrails
  10. Self-Check: Do You Have This Anti-Pattern?
  11. FAQ
  12. What Next

Idea In 30 Seconds

Agents Without Guardrails is an anti-pattern where an agent executes actions without enforced runtime rules: policy gate, allowlist, scope control, budget limits, and clear stop_reason.

As a result, a model error or wrong route becomes a real operational issue: unnecessary calls, unwanted actions, budget overuse, or risky access in the wrong context.

Simple rule: guardrails must live in code and the execution layer, not only in the prompt.


Anti-Pattern Example

The team launches a support agent that can search data, update statuses, and send messages.

The agent gets broad tool access without centralized runtime rules.

PYTHON
decision = agent.next_action(user_message)
result = run_tool(decision.tool, decision.args)
return result

In this setup, baseline control is missing:

PYTHON
# no route-based allowlist
# no deny-by-default for risky actions
# no tenant/env scope enforcement
# no budget/step/time limits

With this approach, the run moves to execution without a policy gate, so risky steps are not stopped at decision time.

In this setup, missing guardrails leads to:

  • unpredictable state changes
  • risk of incorrect or unnecessary calls
  • hard incident analysis without a clear responsibility boundary

Why It Happens And What Goes Wrong

This anti-pattern often appears when the team first optimizes for "making the agent do more" and postpones control boundaries.

Typical causes:

  • too much trust in prompt instructions instead of enforced runtime checks
  • no separate gateway layer for policy decisions
  • mixed roles: the agent both plans and executes risky actions without checks
  • no explicit owner for access rules, limits, and audit

As a result, teams face:

  • risky actions without control - the agent can execute a dangerous step in an invalid scenario
  • resource overuse - without budget and step limits, run cost and latency grow
  • context errors - without scope enforcement, an action can hit the wrong tenant/env
  • blurred responsibility boundaries - incidents are hard to localize between agent and gateway
  • repeated incidents - the team fixes outcomes, but not the root cause

Unlike Write Access by Default, the issue here is broader: not only write limits are missing, but general execution boundaries for the whole run.

Typical production signals that guardrails are weak or missing:

  • potentially dangerous tool calls pass even when approval_required or policy_denied is expected
  • blocked attempts are rare even on risky routes
  • runs often reach max_steps or budget exceedance without early stop
  • audit logs do not show which exact rule allowed or blocked an action
  • the team cannot quickly explain why a specific tool call was executed

Every tool call is an operational decision. If execution boundaries are not enforced, the agent system behaves unpredictably even in simple scenarios.

Correct Approach

Start with a minimal but strict set of runtime guardrails. Every step either passes policy checks or stops the run with clear stop_reason.

If you are starting from scratch, do not try to ship everything at once. Minimum first step: deny-by-default, route-based allowlist, scope enforcement (tenant/env), and explicit stop_reason for policy denials.

Practical framework:

  • deny-by-default for dangerous or unknown actions
  • tool allowlist by role and route
  • mandatory scope (tenant_id, env) from authenticated context
  • step/time/budget limits for every run
  • separate approval step for risky write operations
  • policy decision log for every tool call
PYTHON
ALLOWED_TOOLS_BY_ROUTE = {
    "faq": {"kb.search"},
    "refund": {"order.get", "refund.create"},
}
WRITE_TOOLS = {"refund.create", "ticket.close", "email.send"}


def execute_step(decision, ctx):
    allowed = ALLOWED_TOOLS_BY_ROUTE.get(ctx["route"], set())

    if decision.tool not in allowed:
        return stop("policy_denied:tool_not_allowed")

    if decision.tool in WRITE_TOOLS and not has_approval(ctx, decision):
        return stop("approval_required")

    if exceeds_budget(ctx):
        return stop("budget_exceeded")

    scoped_args = enforce_scope(
        decision.args,
        tenant_id=ctx["tenant_id"],
        env=ctx["env"],
    )
    return run_tool(decision.tool, scoped_args)

In this setup, the agent operates inside controlled boundaries: allowed actions pass, while risky or invalid steps stop transparently.

Quick Test

If the answer to these questions is "yes", you have Agents Without Guardrails anti-pattern risk:

  • Can the agent call a tool without an explicit policy check?
  • For risky steps, do policy_denied or approval_required almost never appear?
  • Can the team not quickly explain why a specific action was allowed?

How It Differs From Other Anti-Patterns

Write Access by Default vs Agents Without Guardrails

Write Access by DefaultAgents Without Guardrails
Main problem: write access is open by default.Main problem: there are no general execution boundaries for a run (policy, scope, budgets, approvals).
When it appears: when write does not pass through deny-by-default and approval-gate.When it appears: when even read/write/tool routes have no enforced runtime rules.

In short: Write Access by Default is about an unsafe write access model, while Agents Without Guardrails is about missing execution boundaries overall.

Blind Tool Trust vs Agents Without Guardrails

Blind Tool TrustAgents Without Guardrails
Main problem: unvalidated tool output feeds decisions.Main problem: the system does not limit which actions the agent can execute at all.
When it appears: when parse/schema/invariant validation gate is missing.When it appears: when policy decisions stay in the prompt, not in the execution layer.

In short: Blind Tool Trust is about data reliability before action, while Agents Without Guardrails is about boundaries of allowed actions.

No Stop Conditions vs Agents Without Guardrails

No Stop ConditionsAgents Without Guardrails
Main problem: the loop has no clear termination conditions.Main problem: system-level execution boundaries for actions, rights, and access are missing.
When it appears: when there is no max_steps, timeout, no_progress.When it appears: when the run does not pass through policy gate, scope check, and access control.

In short: No Stop Conditions is about loop termination control, while Agents Without Guardrails is about broader safe execution boundaries.

Self-Check: Do You Have This Anti-Pattern?

Quick check for anti-pattern Agents Without Guardrails.
Mark items for your system and check status below.

Check your system:

Progress: 0/8

⚠ There are signs of this anti-pattern

Move simple steps into a workflow and keep the agent only for complex decisions.

FAQ

Q: Is it enough to put restrictions in the system prompt?
A: No. Prompt is useful as intent, but not as enforcement. Critical boundaries must be checked in the runtime layer.

Q: Where should we start if guardrails are almost missing?
A: Start with deny-by-default, route-based allowlist, scope enforcement (tenant/env), and explicit stop_reason for policy denials.

Q: Will guardrails make the system too slow?
A: Guardrails add some overhead. But in production this cost is almost always lower than wrong actions, budget waste, and manual incident cleanup.


What Next

Related anti-patterns:

What to build instead:

⏱️ 8 min read β€’ Updated March 17, 2026Difficulty: β˜…β˜…β˜…
Implement in OnceOnly
Safe defaults for tool permissions + write gating.
Use in OnceOnly
# onceonly guardrails (concept)
version: 1
tools:
  default_mode: read_only
  allowlist:
    - search.read
    - kb.read
    - http.get
writes:
  enabled: false
  require_approval: true
  idempotency: true
controls:
  kill_switch: { enabled: true, mode: disable_writes }
audit:
  enabled: true
Integrated: production controlOnceOnly
Add guardrails to tool-calling agents
Ship this pattern with governance:
  • Budgets (steps / spend caps)
  • Tool permissions (allowlist / blocklist)
  • Kill switch & incident stop
  • Idempotency & dedupe
  • Audit logs & traceability
Integrated mention: OnceOnly is a control layer for production agent systems.
Author

This documentation is curated and maintained by engineers who ship AI agents in production.

The content is AI-assisted, with human editorial responsibility for accuracy, clarity, and production relevance.

Patterns and recommendations are grounded in post-mortems, failure modes, and operational incidents in deployed systems, including during the development and operation of governance infrastructure for agents at OnceOnly.