DEV Community

Haley
Haley

Posted on

Inject One Fake Failure Before You Ship the Agent

The last time I reviewed an agent log, the tool returned an empty object. The agent wrote it to the database as "success". Then it moved to the next task. No one was asked to confirm.

Why did the agent keep going? Because its hand-back path was never tested. Failure recovery does not happen on its own. You must design it, build it, and rehearse it.

A hand-back path is the moment a system stops and asks a human for a decision. It should be visible, reversible, and documented. I want to show you a minimal way to test it.

We will use MonkeyCode's free model access and free server tier to host a decision log. Disclosure: This article was prepared as part of MonkeyCode's product outreach. The free tier includes a 10M token allowance, which is enough for small regression runs.

Your first step is simple. Define your stop conditions. Look at real tool outputs from the last month. Find failures your agent actually met. Do not guess.

Common candidates are invalid JSON, permission errors, and empty results. Write a function that maps a response to either proceed or need_human. This becomes your contract.

Here is a tiny simulator you can keep in your repo.

# handback_test.py
import json

def decide(raw_response):
    try:
        parsed = json.loads(raw_response)
    except json.JSONDecodeError:
        return "need_human", "invalid_json"
    if parsed.get("status") == "error":
        error_type = parsed.get("error_type")
        if error_type in ("permission_denied", "empty_result"):
            return "need_human", error_type
    return "proceed", None

cases = [
    ('{"status":"ok","data":{"id":1}}', "proceed"),
    ('{"status":"error","error_type":"permission_denied"}', "need_human"),
    ('{bad json', "need_human"),
    ('{"status":"error","error_type":"empty_result"}', "need_human"),
]

for raw, expected in cases:
    action, _ = decide(raw)
    assert action == expected, f"{raw} -> {action}"

print("all hand-back tests passed")
Enter fullscreen mode Exit fullscreen mode

This script is not a production agent. It is a probe. It encodes the behavior you want the agent to have. If the logic changes, this test will tell you.

Now you need to observe runtime behavior. Deploy a small logging endpoint on your free server. Send one event for every hand-back decision. Keep the payload small and structured.

import requests
from datetime import datetime

def log_handback(agent_state, action, reason):
    requests.post(
        "YOUR_SERVER_URL/log",
        json={
            "agent_state": agent_state,
            "action": action,
            "reason": reason,
            "timestamp": datetime.utcnow().isoformat(),
        },
    )
Enter fullscreen mode Exit fullscreen mode

Replace YOUR_SERVER_URL with your deployed endpoint. Do not log secrets. Log enough evidence for a human to judge the failure. Raw response, tool name, and the agent's plan are useful.

Next, inject a realistic failure. Do not choose a random one. Pick the failure you saw most often in your logs. Use a synthetic response instead of a real tool call. That keeps the test safe and repeatable.

Here is the flow you want to see:

Tool call -> Error -> Detect -> Stop -> Prompt human
                                    -> Log handback
Enter fullscreen mode Exit fullscreen mode

When the agent detects a permission error, it should stop. It should explain why. It should ask for confirmation. It should not silently retry three times.

After the run, check your decision log. Did the agent record need_human? Did it include the reason? Did it include enough context for action?

A good hand-back event looks like this:

handback_event:
  tool: "user_directory.remove"
  error_type: "permission_denied"
  raw_response: "Access to /api/users denied"
  agent_state: "before_deletion"
  action: "need_human"
  approved_by: null
Enter fullscreen mode Exit fullscreen mode

This is a reviewable artifact. A human can see the failure, judge the impact, decide, and record the response.

Do not forget accessibility. The hand-back prompt cannot rely on color alone. Use text, icons, and clear labels. Make sure the review interface works with keyboard navigation. Inclusive design applies to failure recovery too.

If your agent never triggers a hand-back, that is a signal. Your stop conditions may be too narrow. Or your tool outputs are more unstable than you think. Fix this before deployment.

This method has limits. It tests synthetic failures, not real ones. Real failures arrive together and bring noise. The test will not catch everything. It gives you a minimal safety net.

You should not use this approach if your agent only reads data. Read-only paths can often use simple retries. Skip it when your platform already handles retries and recovery with proper logging.

Run this test after every change to your agent. Add it to your CI pipeline. Make it a habit.

Inject one fake failure into your agent today. Watch what it does next. If it keeps moving, your product has a bug you haven't shipped yet.

Top comments (0)