Error-Driven Development
Error-driven development treats each compiler error, test failure, or runtime exception as a precise, machine-generated specification: it states what is wrong, where, and what the system expected instead. The loop is to make a change, run verification, hand the first error to the agent with targeted context, and fix its root cause.
CI is red. Fourteen TypeScript errors, three failing tests, and a deprecation warning. The stack trace points at payment.ts:212, you have already burned twenty minutes guessing which of the last six commits broke it, and the fix you just pushed turned one failing test into three. Staring harder at the error is not working.
The two instincts at this point are both wrong. Fixing everything by hand ignores what the agent is actually good at. Pasting the whole log in with “fix everything” gives it too much at once and produces cascading “fixes” that introduce new problems.
The error message is the most precise specification you have of what is wrong, and it is exactly the input an AI assistant is best at consuming — better than any prompt you could write from scratch. Error-driven development leans into that: instead of aiming for a perfect first draft, run a tight error → fix → re-run loop and let each failure steer the next change. Done deliberately, it converges fast even on gnarly cascades.
What error-driven development gives you
Section titled “What error-driven development gives you”- A repeatable error-fix-rerun loop you can run in any of the three tools
- Copy-paste prompts for a production stack trace, a compiler cascade, and a failing-test loop
- The per-tool mechanics: who runs the tests, who rolls back a bad fix, who iterates unattended
- A way out when fixing one error keeps producing three more
- An MCP shortcut that pulls the Sentry issue for you instead of copy-pasting stack traces
- A habit for turning a hard-won fix into a rule in
CLAUDE.md,.cursor/rules, orAGENTS.md
The error-fix-rerun loop
Section titled “The error-fix-rerun loop”Rather than trying to prevent every error, lean on them as the fastest feedback mechanism available:
- Make a change (or let the agent make one)
- Run the verification step (build, test, lint, type-check)
- Read the first error
- Feed it to the agent with targeted context
- Let the agent fix the root cause
- Repeat from step 2
The critical discipline is one error at a time. Error messages cascade: a single root cause produces dozens of downstream errors, and fixing the first one usually makes half the others disappear. Fixing error fourteen while error one is still there is wasted work.
Handing an error to each tool
Section titled “Handing an error to each tool”The cycle is identical everywhere — surface an error, give the agent enough context, apply the fix, re-run the exact thing that failed. What differs is who runs the command and how you back out a bad fix.
In agent mode, Cursor runs the tests or build itself, reads the terminal output, and iterates without you copy-pasting — after a failing command you can simply say “fix the error in the terminal.” The safety net is checkpoints: every agent edit is a restore point, so when a fix makes things worse you roll back to the last green state in one click instead of untangling it.
When you do paste an error, paste it with the context that makes it actionable:
I ran npm run type-check and got this error:
src/services/notification.ts:42:5 - error TS2345:Argument of type 'string' is not assignable to parameterof type 'NotificationPayload'.
Fix the root cause. The function signature in @src/services/notification.tsexpects a NotificationPayload object, not a raw string. Check the callerat line 42 and the type definition in @src/types/notification.ts.Best when you want to watch the loop happen and intervene the moment it goes sideways.
Claude Code runs the compiler and tests through the Bash tool and feeds the output back to itself, so the loop closes inside one session:
Run npm run type-check. For each error:1. Read the file and line number referenced in the error2. Understand the root cause (don't just suppress the error)3. Fix the root cause4. Re-run type-check to verify the fix5. Move to the next error
Fix errors one at a time. Do not batch fixes.You can also pipe the output straight in:
npm run type-check 2>&1 | claude -p "Fix the first TypeScript error. Show me the root cause and your fix."In headless mode (claude -p) the loop can iterate against a failing command with a turn cap so it does not spin forever. Best when the loop should run from the terminal or in CI — scripted and bounded.
Codex runs commands inside a sandbox. For an interactive loop, use codex --sandbox workspace-write -c approval_policy=on-request: the sandbox permits routine edits and test commands, and on-request is a separate escalation policy rather than a failure hook — so ask Codex in the prompt to stop and report after a bounded number of failed attempts.
Run npm run type-check. Fix each error one at a time,starting with the first one. After fixing each error,re-run the check to see if downstream errors resolved.Do not batch fixes -- address one root cause per iteration.In ChatGPT desktop you can paste terminal output directly as context, and for CI failures the GitHub integration reads the failing check output straight from the PR. Best when you want it to grind through a long error list mostly unattended but stop at genuine failures.
The loop in three real situations
Section titled “The loop in three real situations”A production bug from a stack trace
Section titled “A production bug from a stack trace”A user hit a crash and your error tracker captured the exception. The fastest path is to hand over the trace plus the files it implicates — the full exception and stack, not just the top line, because the top frame is where the value was read, rarely where it went wrong.
The fix should address where total became undefined — an upstream cart with no items, say — not bolt an ?. onto line 212. Re-run the failing path; if a new error surfaces, feed it back and repeat.
A compiler cascade after a refactor
Section titled “A compiler cascade after a refactor”You changed the signature of a core function and the type-checker lit up with thirty errors across the codebase. This is the loop’s sweet spot, because the errors are an exact, machine-generated worklist. Do not fix anything by hand first; let the full list materialize, then delegate the whole thing.
The agent fixes call sites, re-runs the type-checker, and repeats. An hour of tedium by hand finishes in a few cycles, because the agent never gets bored on call site twenty-seven.
A failing test as the oracle
Section titled “A failing test as the oracle”The strongest form of the loop is to write the failing test first and let the agent drive itself green against it. The test is an unambiguous oracle, so the loop terminates on its own.
That last sentence matters. Without it, an over-eager agent will sometimes “fix” the failure by loosening the assertion. Pin the test as the spec.
Breaking a cascade that will not converge
Section titled “Breaking a cascade that will not converge”A compiler worklist shrinks as you work it. The dangerous kind of cascade does the opposite: the agent fixes error 1, which introduces error 2, which when fixed introduces error 3, and you burn the context window on whack-a-mole. The tell is the same file appearing in every round. When that happens, stop fixing errors and go after the structure.
When the same file keeps appearing, zoom out:
We've been fixing errors in notification.ts for three iterationsand new ones keep appearing. Stop fixing individual errors.
Instead:1. Read the full file @src/services/notification.ts2. Read the types it depends on @src/types/notification.ts3. Read the test file @src/services/__tests__/notification.test.ts4. Identify the structural problem causing the cascade5. Propose a fix for the root architectural issue
Do not modify any files until I approve the approach.Use /clear to reset context when caught in a loop, then restart with a broader view:
I've been fixing TypeScript errors in src/services/notification.tsbut they keep cascading. Take a step back:
1. Read the file and all its imports2. Run npm run type-check and capture ALL errors related to this file3. Identify the common root cause4. Fix the structural issue rather than individual symptoms5. Re-run type-check to verifyAfter more than two correction cycles on the same error, the context is polluted with failed approaches. /clear plus a fresh prompt will almost always converge faster than pushing on.
Start a fresh thread when errors cascade. The clean context helps Codex see the structural issue:
The file src/services/notification.ts has cascading TypeScript errors.Individual fixes keep introducing new errors.
Read the file and all its dependencies. Identify the structuralroot cause and fix it holistically. Run type-check after yourchanges to verify all errors are resolved.Turning errors into permanent knowledge
Section titled “Turning errors into permanent knowledge”The most valuable errors are the ones you never see again. When an error turns out to be non-obvious, capture the lesson in the project’s configuration so the agent avoids the same mistake in every future session.
Add a project rule in .cursor/rules/errors.md:
---description: "Common errors and their fixes for this project"alwaysApply: true---
## Known Error Patterns
- When modifying notification types, always update both `src/types/notification.ts` AND the Zod schema in `src/validators/notification.ts`. They must stay in sync.- Redis connection errors in tests: run `docker compose up -d redis` before running the test suite.Add the lesson to your CLAUDE.md, or ask Claude to remember it:
Remember: when modifying notification types, always update bothsrc/types/notification.ts AND the Zod schema insrc/validators/notification.ts. They must stay in sync.Claude’s auto memory stores this in ~/.claude/projects/<project>/memory/ and loads it in future sessions. Putting it in the project’s CLAUDE.md instead gives the whole team the same benefit.
Add the lesson to your AGENTS.md:
## Known Error Patterns
- Notification types: always update both src/types/notification.ts AND the Zod schema in src/validators/notification.ts together.- Redis must be running for integration tests: docker compose up -d redisCodex reads AGENTS.md at the start of every session, so the guidance applies automatically to all future work.
Where the error-fix-rerun loop breaks down
Section titled “Where the error-fix-rerun loop breaks down”- Chasing the wrong error in a cascade. The first error often causes the rest. Tell the agent to fix the earliest root error and re-run before touching the others.
- Fixing the symptom, not the cause. Wrapping an
undefinedaccess in?.stops the crash without explaining why the value was missing. Ask “where did this become undefined?”, not “stop the throw.” - The agent suppresses instead of fixing.
@ts-ignore,as any, empty catch blocks, andeslint-disablein the output all mean symptom management. Be explicit: do not suppress errors, fix the underlying issue. - Looping forever on a flaky test. A non-deterministic failure lets the agent “fix”, re-run, see green, and declare victory — then it flakes again in CI. Quarantine the flake first; do not run the loop against it.
- Edit-the-test escape hatch. Agents sometimes make a failing assertion pass by weakening it. Add “do not modify the test” to any test-driven prompt.
- Context blindness. Pasting only the top stack frame hides the real culprit deeper in the trace. Give the full trace and name the suspect files.
- Too many errors to process. If the type-checker produces 200+ errors, do not feed them all in. Work the first three to five — they are usually the root causes — then re-run and see how many remain.
- The error message is genuinely opaque. Segfaults and generic “internal server error” carry no specification. Switch from error-driven to hypothesis-driven: add logging, reproduce, narrow down by elimination.
- The error is in generated code. When the broken file is one the agent wrote from scratch, deleting it and regenerating from a more constrained prompt usually beats patching it.