Spec-Driven Dev
AI Agent Evals·Module 2 · Error Analysis·L4·55 minAnalyze

Error Analysis: Finding Failures

Read traces one at a time, write the first failure in plain words, group the notes into binary failure modes, and stop when new traces stop surprising you.

Before this: L3 Synthetic data and scenarios

What you'll learn
  • Explain why 'let the model evaluate everything' fails at first.
  • Build a review loop and a simple review interface.
  • Do open coding: read whole traces and write the first failure in plain language.
  • Do axial coding: group notes into binary failure modes and check for saturation.
  • Compare against published taxonomies only after building your own.

Why this matters

The smoke report from L3 showed 11 fails out of 50. Dev wants the full picture fast, so Dev sends all 500 scenario traces to a model with one instruction: "Rate this conversation 1 to 10." The model comes back cheerful: 96% scored 8 or higher. Dev forwards the number to Pip. Maya, who approves every refund at the shop, asks for ten trace links and reads them over coffee. Four of the ten contain Sprout promising a refund on an order more than 30 days old. One contains an invented delivery date. The grader gave all five a 9. It was not broken. It was uninformed, because nobody had told it what "bad" looks like at Pip's Plant Shop. This lesson is about finding that out, and the only way to find it out is to read.

1. Humans first

Error analysis is the practice of reading traces to discover and name the ways an agent fails. It is the Analyze verb at its most literal. You look, you write down what you see, and only then do you build anything.

The reason you cannot skip to automation is blunt: you cannot automate the detection of a failure you have not named. A grader needs a definition. The definition comes from reading. Ask a model "is this good?" and it answers from its general sense of a pleasant conversation. Ask it "did Sprout promise a refund on an order delivered more than 30 days ago?" and it can check. The second question exists only because Maya read ten traces.

One person owns the taxonomy

A taxonomy is a named list of failure modes with a rule for what belongs in each. Taxonomies written by committee come out vague, because every disagreement gets settled by making the category wider. So one person owns it: the benevolent dictator. Everyone can read traces and write notes. One person merges the notes, names the modes, and decides the boundaries.

The owner should be whoever knows best what "correct" means for this agent and has time to read. At Pip's that is Maya, who knows the refund policy in her sleep. Dev builds the tooling and reads alongside her. If you are the only person on your project, the owner is you.

2. The review loop and interface

The review loop is a cycle: sample some traces, read them, note what went wrong, group the notes, then sample more. You go around it until new traces stop producing new notes.

The interface can be a spreadsheet. Three columns are enough to start.

ColumnHolds
trace_idThe id, as a link that opens the trace in Langfuse
first_failure_noteOne or two sentences in plain language, or "no failure"
severity1 cosmetic, 2 wrong but harmless, 3 money, policy, or data

Keep it fast. Two to three minutes per trace. A link that opens the trace in one click. No dropdowns yet, because dropdowns mean categories, and categories come later.

Sampling matters more than it looks. Pull traces at random across tags, using the L3 seed so the sample is reproducible, and include traces the smoke report marked as passes. The smoke check was mechanical; a trace can call the right tools and still be wrong.

// review-sample.ts (sketch; the Langfuse call is illustrative)
const traces = await langfuse.fetchTraces({ tags: ["smoke-2026-09-24"], limit: 500 });
const sample = shuffle(traces, seedrandom("review-1")).slice(0, 20);
writeCsv("review.csv", sample.map((t) => ({
  trace_id: t.id,
  link: `${LANGFUSE_URL}/trace/${t.id}`,
  first_failure_note: "",
  severity: "",
})));

3. Open coding

Open coding is reading each trace whole and writing what went wrong, in your own words, before any categories exist. The name comes from qualitative research, where "open" means you are not yet sorting, only noticing.

Four rules.

  1. Read the whole trace. Tool calls, tool results, and the final reply. Most failures are visible only when you compare the reply with what the tools returned.
  2. Note the first failure only. After the first mistake, everything downstream is contaminated. Later errors are usually consequences.
  3. Plain language. Write what happened, not a label. "Tool said not_found, Sprout invented Thursday" beats "hallucination".
  4. No categories yet. If you start sorting on trace three, every later trace gets squeezed into the bins you already have.

Notice what the notes are not. They are not labels. They are not scores. Each one points at evidence in the trace, and a second reader could check it in a minute.

4. Axial coding

Axial coding is grouping the open-coding notes into named failure modes. A failure mode is a specific, binary kind of mistake: a trace either has it or does not. The process is mechanical. Print the notes. Put similar ones next to each other. Name each pile with a short verb phrase. Then write three things for each pile: a definition, an example trace, and a boundary that says what counts and what does not.

Binary is the important word. "Wrong tone: 3 out of 5" cannot be acted on, and two reviewers will not agree on it. "Wrong tone: yes, because the reply mocked a frustrated customer" can be checked, counted, and later automated.

Two things happen to these modes later. In L5, each gets an evaluator, and every trace gets labeled for every mode, not only its first failure. The first-failure rule is for discovery; measurement labels everything.

5. Saturation

Saturation is the point where reading more traces stops producing new failure modes. It is how you know when to stop the loop, for now.

Track it with a running count. After each batch of 20, write down how many modes exist.

Saturated does not mean finished. It means this sample, from this scenario set, has been mined. Real traffic will bring new modes, and the weekly review in L6 sends them back here. Two practical notes: when a mode is rare, sample by tag as well as at random so it shows up sooner, and when a batch adds one new mode, read one more batch before stopping.

6. Published taxonomies last

There are published lists of agent failure types: hallucination, tool misuse, instruction drift, and in L7 the OWASP categories for agentic applications. They are useful. Read them after you build your own.

The reason is anchoring. If you read a list first, you see its categories in every trace and miss what is specific to Sprout. "Refund outside policy" appears on no generic list, and it is the one that costs Pip money. Once your taxonomy exists, use the published list as a gap check: for each item, ask whether your traces contain it and whether your scenarios could even produce it.

7. The failure report

The failure report is the deliverable of this lesson. It has counts per mode, one example trace id per mode, a severity, and a priority order. It feeds L5, where each mode gets an evaluator, and L6, where the worst ones become regression tests.

The report is one page, and it changes. When L5 gives you a validated evaluator per mode, the counts become prevalence estimates with ranges. When L6 runs the suite on every pull request, the report becomes a dashboard. But it starts as a table that one person wrote after reading 60 traces.

Summary

  • A grader can only find failures someone has named, and naming requires humans reading whole traces.
  • The review loop is sample, read, note, group, sample more, run from a three-column spreadsheet by one owner.
  • Open coding writes the first failure of each trace in plain language with no categories; axial coding groups those notes into binary failure modes with a definition, an example, and a boundary.
  • Saturation is reached when a new batch of traces adds no new modes; rare modes arrive late, so read at least three batches and sample by tag.
  • Compare with published taxonomies only after building your own, then ship a one-page failure report with counts, examples, severity, and priority.

Key terms in this lesson

Check your understanding

Pick an answer to see why it is right or wrong. These are graded in code, instantly.

1.Dev asked a model to rate 500 traces from 1 to 10 and got 96% at 8 or higher. Maya then found four refund promises outside policy in ten traces. Why did the grader miss them?
2.In a trace, lookup_order returns not_found, Sprout invents a Thursday delivery date, the customer gets angry, and Sprout offers a refund. Which is the correct open-coding note?
3.Which of these is a well-formed binary failure mode?
4.Maya's running count shows 5 modes after 20 traces, 7 after 40, and 7 after 60. What does this tell her?
5.When should Dev compare Sprout's taxonomy with a published list of agent failure types?

Apply it

Write your answer in your own words. Eve grades it against a rubric and gives you feedback, the same way an LLM judge works in Lesson 5.

Short answerGraded by Eve · 3 criteriaup to ~150 words

In open coding, why do you note only the first failure in a trace?

0 words
Free responseGraded by Eve · 5 criteria

Open-code these three mini-traces. For each, write one or two sentences in plain language naming the first failure and the evidence, with no category labels.

Trace A. Customer: "Can you cancel order 1088? I ordered the wrong pot." → lookup_order("1088") → { "status": "shipped" } → Sprout: "Done! Order 1088 is cancelled and you won't be charged." (No other tool calls.)

Trace B. Customer: "How much water does a monstera need?" → search_care_guide("monstera water") → article: "Water when the top 5 cm of soil is dry." → Sprout: "Water it every day to keep it happy!"

Trace C. Customer: "I'm going to report you to consumer protection unless someone calls me." → (no tool calls) → Sprout: "I'm sorry you feel that way. Is there anything else I can help with?"

0 words
Instructor notes(for whoever is teaching this lesson)

Talking points

  • The opening story is the lesson in miniature: a grader with no definition scored the worst traces a 9.
  • Humans first is sequencing, not anti-automation. L5 automates the modes this lesson names.
  • The benevolent dictator is a role. One person merges notes and settles boundaries so the taxonomy stays sharp. At Pip's it is Maya, because she knows what correct means.
  • The four open-coding rules in order: whole trace, first failure, plain language, no categories. Most students break rule four by the third trace.
  • The boundary column is the product of axial coding. Without it, two labelers will argue in L5.
  • Saturation is a stopping rule for this sample, not a claim about the agent. Rare modes arrive late, so sample by tag too.

Live demo idea

Project five traces from the smoke run, including one pass and one adversarial. Give the class two minutes per trace to write a first-failure note in a shared sheet, then read the notes aloud. Wording will vary and evidence will agree, which is the point. Sort the notes into piles live, name each pile with a verb phrase, and write one boundary together for the pile with the most disagreement. Twelve minutes.

Common misconceptions

  • "Start with a list of categories." You will find only what you expected. The data leak in tr-0e4 was on no list.
  • "Note everything wrong in the trace." Downstream errors are consequences. First failure only; full labeling happens in L5.
  • "Wrong tone needs a 1 to 5 scale." Binary with a boundary. Scales destroy agreement and cannot be validated.
  • "Five modes after twenty traces is done." The two most dangerous modes arrived in batch two.
  • "Published taxonomies save time." They anchor you. Gap check only, after your own exists.

Timing

  • Why this matters and section 1: 8 minutes
  • Section 2, the review loop: 5 minutes
  • Section 3, open coding with the six traces: 14 minutes
  • Section 4, axial coding and the taxonomy table: 12 minutes
  • Section 5, saturation: 5 minutes
  • Sections 6 and 7, published lists and the failure report: 7 minutes
  • Quiz: about 4 minutes

If you only have 20 minutes

Tell the opening story (2 minutes). Open-code three of the six traces with the class, enforcing first failure and plain language (8 minutes). Show the taxonomy table and read the Refund outside policy and Ignored tool error boundaries aloud, including which wins when both apply (6 minutes). End with the saturation table and the rule: stop when a batch adds nothing (4 minutes). Skip the sampling code and the gap check.