PromptABCD
FeaturesLearnHow it worksUse casesFAQGuideBlogContext Blocks
Sign inGet started free
Sign inSign up
PromptABCD

A calm home for your best AI prompts. Save them once, find them in seconds, reuse them forever.

Product

  • Features
  • Chrome Extension
  • Free Courses
  • How it works
  • Use cases
  • Blog
  • Context Blocks
  • Export Anywhere
  • FAQ

Resources

  • User guide
  • Learn prompting
  • Sign in
  • Get started free

© 2026 PromptABCD. All rights reserved.

Privacy PolicyTerms and Conditions
Home/Blog/AI Harness/Error Taxonomy: Classifying Harness Failures
AI Harness

Error Taxonomy: Classifying Harness Failures

When every failure looks the same, you can't retry, route, or alert correctly. Agent harness error classification gives failures types that drive real behavior.

September 10, 2026·8 min read
ShareShare
⚡Featured Prompt— copy and use right now
def run(prompt):
    try:
        return agent.execute(prompt)
    except Exception as e:
        log.error(f"run failed: {e}")
        return retry(prompt)          # retry ALL failures, indiscriminately

When was the last time your agent failed and you could tell, instantly, what kind of failure it was — a model problem, a tool problem, a bug in your harness, or a bad input from the user? For most teams the answer is "never, they all look the same," and that's the exact problem agent harness error classification solves. When every failure is just a generic exception, you can't route them, can't retry the right ones, can't alert on the ones that matter, and can't tell a flaky tool from a broken harness. This teardown takes a harness with no error structure and rebuilds it with a taxonomy that drives real behavior.

The core idea: errors aren't all the same, and treating them the same throws away the single most useful piece of information you have about a failure — its category. A rate limit and a null-pointer bug are both "exceptions," but everything you'd want to do about them is different.

Before: The Weak Prompt

Here's the error handling most harnesses start with — catch everything, log it, maybe retry, move on.

hljs python
[object Object], ,[object Object],(,[object Object],):
    ,[object Object],:
        ,[object Object], agent.execute(prompt)
    ,[object Object], Exception ,[object Object], e:
        log.error(,[object Object],)
        ,[object Object], retry(prompt)          ,[object Object],

What this does: Catches any exception, logs a generic message, and retries. It treats a transient rate limit, a malformed user input, a hung tool, and a genuine bug in your own code as if they were the same event. They are not, and handling them identically is wrong for at least three of the four — the only one a blanket retry serves correctly is the transient model error, and it happens to be the least urgent of the bunch.

Why It Fails

Retrying indiscriminately is the first problem. A rate limit should be retried with backoff. A malformed input should not — it'll fail identically every time, and retrying just wastes calls and delays the inevitable error. A bug in your harness definitely shouldn't be retried; it needs a human. Blanket retry does the wrong thing for most failure types.

The second problem is invisibility. When every failure logs "run failed," your error metrics are a single meaningless number. You can't see that tool errors spiked while model errors stayed flat, because you never distinguished them. The most important signal in your failures — the pattern across categories — is erased before it's recorded.

The third is misrouting. Different failures belong to different owners. A model overload is the provider's problem and resolves itself; a tool timeout might be an infrastructure issue; a validation error is a user problem; a harness exception is your bug. With no classification, every failure lands in the same undifferentiated pile, and the ones that need urgent attention hide among the ones that don't. The practical effect is that on-call engineers either get paged for every transient rate limit until they mute the alert entirely, or they mute it and then miss the real bug — both outcomes flow directly from refusing to distinguish the categories.

⚠️ Common mistake: Catching

Exception
broadly and treating the result uniformly. The broad catch is where the information dies — the moment you flatten every failure into one type, you've discarded the category that would have told you what to do. Catch specific types, or classify immediately inside the catch.

After: The Improved Prompt

The rebuilt harness defines a taxonomy of error types and makes each one carry its own retry policy and severity, so classification drives behavior automatically.

hljs python
[object Object], ,[object Object],(,[object Object],):
    retryable = ,[object Object],
    severity = ,[object Object],

,[object Object], ,[object Object],(,[object Object],):        ,[object Object],
    retryable = ,[object Object],
    severity = ,[object Object],

,[object Object], ,[object Object],(,[object Object],):         ,[object Object],
    retryable = ,[object Object],                   ,[object Object],
    severity = ,[object Object],

,[object Object], ,[object Object],(,[object Object],):   ,[object Object],
    retryable = ,[object Object],                  ,[object Object],
    severity = ,[object Object],

,[object Object], ,[object Object],(,[object Object],):     ,[object Object],
    retryable = ,[object Object],
    severity = ,[object Object],              ,[object Object],

What this does: Defines a hierarchy where each error type declares whether it's retryable and how severe it is. A

ModelError
retries and logs a warning; a
ValidationError
fails fast and logs as informational; an
InternalError
fails and pages someone. The behavior falls out of the type — you no longer decide what to do at each catch site, because the taxonomy already encodes it.

Breaking Down Each Element

Each error class maps to a distinct owner and response.

ModelError
is provider-side — overloads, rate limits, timeouts talking to the model. Retryable with backoff, low urgency, usually self-resolving. You want these counted but not paged.

ToolError
is a tool that failed — a network blip, a downstream timeout, a service that's momentarily down. Often transient, so retryable, but a sustained spike in one tool's errors is a real signal worth alerting on.

ValidationError
is bad input or bad arguments — the user asked for something impossible, or the model produced arguments that failed validation. Not retryable, because the same input fails the same way. These belong back to the caller as a clear error, fast.

InternalError
is the important one: a bug in your harness. Not retryable, maximum severity, page a human. Distinguishing this class is the whole point — it's the failure that won't fix itself and that you most need to see immediately, and in the flat-exception world it drowned among the transient noise.

hljs python
[object Object], ,[object Object],(,[object Object],):
    ,[object Object],:
        ,[object Object], agent.execute(prompt)
    ,[object Object], HarnessError ,[object Object], e:
        metrics.increment(,[object Object],, tags={,[object Object],: ,[object Object],(e).__name__,
                                              ,[object Object],: e.severity})
        ,[object Object], e.retryable ,[object Object], under_retry_budget():
            ,[object Object], retry(prompt)
        ,[object Object],

What this does: Classifies at the catch site by type, records the failure with its type and severity as metric dimensions, and retries only if the error declares itself retryable and there's budget. One handler now does the right thing for every category, and every failure is counted under its true type — so your dashboards finally show which kind of failure is happening.

⚡ Pro tip: Make the error type a dimension on every failure metric. "Errors are up" is useless; "ToolError is up 5x while everything else is flat" points straight at the problem. The taxonomy isn't just for routing — it's what turns your error dashboard from a single scary line into an actionable diagnosis of exactly what's failing.

Variations for Different Contexts

For user-facing agents, add a

UserError
class distinct from
ValidationError
for things the user can fix by rephrasing — and surface those helpfully rather than logging them as failures at all. Not every non-success is an error worth alerting on.

For agents with critical tools, subclass

ToolError
per tool tier so a failure in a payment tool is more severe than one in a search tool. The taxonomy can be as granular as your operational needs require.

For multi-agent systems, add error classes for cross-agent failures — a sub-agent that failed, a coordination timeout — so failures in one agent don't masquerade as bugs in another. Classification scoped to the architecture keeps blame accurate.

⚠️ Common mistake: Classifying errors but then not wiring the classification to behavior — defining nice error types and still retrying everything and alerting on nothing. The taxonomy is only worth building if

retryable
actually gates retries and
severity
actually routes alerts. A classification that doesn't drive behavior is just fancier logging — nicer to read, but no more useful than the generic exceptions you started with.

Turning Classification Into a Feedback Loop

The payoff of agent harness error classification compounds once the typed failures start flowing into your metrics, because now your errors form a dataset you can learn from instead of a wall of noise you tune out. Over weeks, the distribution across types tells you where to invest: a steady baseline of

ModelError
is just life with a provider, but a climbing
ToolError
rate for one specific tool is a reliability problem with a clear owner, and any
InternalError
at all is a bug that jumped the queue.

That last point is worth dwelling on. Because

InternalError
is a class of its own, you can alert on it with zero tolerance — one occurrence pages someone — while letting the transient classes ride their normal noise. Before classification, an internal bug produced the same "run failed" line as a rate limit and got the same shrug; agent harness error classification is what lets the bug that needs a human actually reach one.

hljs python
[object Object], ,[object Object],(,[object Object],):
    counts = metrics.query(,[object Object],, group_by=,[object Object],, window=window)
    ,[object Object], {t: {,[object Object],: c, ,[object Object],: ROUTING[t]} ,[object Object], t, c ,[object Object], counts.items()}
    ,[object Object],
    ,[object Object],

What this does: Aggregates failures by type over a window and pairs each with its routing action, producing a report that says not just what failed but what to do about each category. This is the operational view classification unlocks — a triage list instead of a body count. The counts point you at the problem; the routing tells you who owns it.

⚡ Pro tip: Review your error-type distribution weekly, not just when something breaks. The classes that are slowly trending up — a tool that's failing 2% more each week — are the incidents you can prevent instead of respond to. A flat error dashboard hides these trends; a per-type one surfaces them while they're still small enough to fix calmly.

Save and Reuse This

An error taxonomy is a design that every agent in your fleet should share, because inconsistent error classes across services mean your cross-agent dashboards and alerting can't line up — one service's "ToolError" is another's generic exception, and the aggregate is noise. Keep your error hierarchy and its retry-and-severity policies versioned alongside your prompts in a library like PromptABCD, so every agent classifies failures the same way, your fleet-wide error metrics actually compose, and the critical bug never again hides in the transient noise. A shared taxonomy is what lets you build one alerting rule, one dashboard, and one on-call runbook that work across every agent you run — instead of a different, incompatible error scheme per service that no aggregate view can ever reconcile.

ai-harnesserror-handlingtaxonomyretriesobservabilityreliability

Continue Reading

Managing Prompt Templates Across a Harness Codebase
AI Harness

Managing Prompt Templates Across a Harness Codebase

Four divergent copies of one prompt caused a two-day bug. Harness prompt templates management makes prompts versioned, tested, single-source artifacts instead of scattered strings.

September 10, 2026·8 min read
How to Open-Source Your Agent Harness
AI Harness

How to Open-Source Your Agent Harness

An agent harness isn't an ordinary library — it's security-sensitive infra tangled with your secrets. Release an open source agent harness without leaking a key or shipping unusable code.

September 10, 2026·8 min read
Building a CLI Around Your Agent Harness
AI Harness

Building a CLI Around Your Agent Harness

A useful agent nobody could run became one everyone uses — the fix was a real agent harness cli with stdin, clean stdout, and meaningful exit codes.

September 10, 2026·8 min read

Save the prompts from this post

PromptABCD is a free prompt manager. Paste, organize, and reuse your best AI prompts — no more hunting through chat history.

Start free →
← PreviousBuilding a CLI Around Your Agent HarnessNext →How to Open-Source Your Agent Harness
Share this post:
ShareShare