Back to writing
LLM InfrastructureAgentsArchitecture

Your Agent Broke at 2 AM. Now What?

Jun 18, 2026 · 8 min read

Why debugging agentic systems is a fundamentally different problem — and what I've built to deal with it.


In my last post, I talked about what makes a system genuinely agentic — the autonomous loop, the critic, the falsifier, the layered memory. If you haven't read it, start there. This post assumes you've already built something real.

Because here's what nobody tells you after the demo works: the hard part isn't building the agent. It's figuring out what went wrong when it breaks at 2 AM and no one can explain why.

I've debugged enough agentic systems in production to know that the standard toolkit doesn't work here. Application monitoring tracks errors and latency. That's useful for APIs. It's nearly useless for parsing agent logic — why an agent chose path A over path B, why the critic flagged a result on the fifth attempt but not the third, why the output looked fine structurally but was semantically wrong.

Agentic systems fail differently. And they need to be debugged differently.


Standard observability wasn't built for this

Let me be specific about what breaks down.

In a traditional application, a request comes in, hits a few services, and returns a response. You trace the request, you measure latency at each hop, you check for errors. If something fails, the stack trace usually tells you where.

In an agentic system, a single task might trigger an orchestrator, which delegates to an agent, which calls two tools, generates a result, passes it to a critic, gets rejected, retries with different context, passes again, gets sent to a falsifier, gets flagged on an edge case, loops back to the original agent with feedback, and finally returns a result that's different from what any single step would have produced alone.

Standard monitoring sees this as "a request that took 47 seconds and made 14 API calls." It can tell you it was slow. It can't tell you why the agent changed its approach on the third attempt, or what the critic saw that the agent missed, or whether the final result was actually better than the one that got rejected on the first pass.

That's the gap. You need tools that understand agent logic, not just infrastructure metrics.


Correlation first, everything else second

The first architectural decision I made that actually helped with debugging was simple: every multi-agent run starts with a single correlation ID, and that ID flows through every agent, every tool call, every critic pass, every retry.

Each agent also gets its own agent ID. So when something fails, the first question is easy to answer: which run, which agent. Not "somewhere in the system" — this specific run, this specific agent, this specific step.

It sounds basic. But I've reviewed agentic systems where logs from different agents land in different places with no shared identifier. When something breaks, the team manually correlates timestamps across log streams to reconstruct what happened. That's not debugging. That's archaeology.

The correlation ID is the thread you pull. Everything else hangs off it.


You need to log things that don't exist in traditional systems

Here's where agentic observability diverges from standard application logging. In a typical system, you log requests, responses, errors, and maybe some business events. In an agentic system, that's not enough. You need to capture the decision-making context.

What I log on every agent run, tied to the correlation ID:

Which prompt version was used?and Not just "agent X ran" but "agent X ran with prompt v2.3." When a prompt changes and behaviour degrades, you need to trace the exact version that was active during the failure. Without this, you're guessing whether the problem is the prompt, the model, or the data.

The context window snapshot. What did the agent actually see when it made its decision? What was in the context, what was the token count, and was the context full or sparse? This matters more than most people realise, and I'll come back to why.

Tool inputs and outputs. Every tool call — what went in, what came back, how long it took. Tool latency matters because a slow tool response can cascade through the entire loop. And tool output matters because a subtly wrong tool response will produce a confidently wrong agent result, and you won't know where the error originated unless you logged both sides.

Memory state — in and out. What did the agent read from semantic memory? What did it write to episodic memory? Did the memory retrieval return relevant context or noise? Memory-related failures are some of the hardest to debug because the agent's behaviour looks "correct" given what it knew — the problem is that what it knew was wrong or incomplete.

All of this gets emitted in OpenTelemetry format. That's a deliberate choice — OTel is vendor-agnostic so that the same trace data can flow into Langfuse for LLM-specific analysis or into any standard observability sink for infrastructure-level monitoring. You don't want to be locked into a single tool when you're debugging a system this complex.


Enforce structure between agents. No exceptions.

This is the debugging lesson that cost me the most time to learn: never pass raw LLM output from one agent to the next.

LLMs return text. Text is ambiguous, inconsistent, and occasionally hallucinated. When you pass verbatim LLM output to a downstream agent, you're asking that agent to parse natural language, infer structure, and hope for the best. Sometimes it works. In production, "sometimes" isn't good enough.

What I enforce instead: typed contracts between agents. Every agent-to-agent handoff goes through a defined JSON schema, validated with Pydantic. The output of agent A must conform to a structure that agent B expects. No exceptions.

Here's why this matters for debugging: when schema validation fails, it's a signal. It means the upstream agent didn't complete its task properly. Maybe it hallucinated a field. Maybe it did half the work and skipped the rest. Maybe the context was insufficient and the agent filled in gaps with garbage. Whatever the reason, the schema violation is a concrete, traceable failure — not a subtle semantic error that propagates silently through six more agents before someone notices the final output is wrong.

The rule is fail fast. If the schema doesn't validate, either retry with a strict limit or fail the task. Do not pass malformed data downstream. Do not try to "fix" the output with another prompt. And critically, do not allow infinite retry loops. I set hard limits: three attempts, then fail with a clear error that includes the correlation ID, the agent ID, the expected schema, and what actually came back. That log entry alone tells you exactly where to start looking.


Loop detection and context problems

Two failure modes that are unique to agentic systems and will burn you if you don't design for them:

Loop detection. An agent retries, the critic rejects, the agent retries with slightly different wording, the critic rejects again, and you're stuck in a cycle that burns tokens and never converges. This happens more often than you'd think, especially when the critic's evaluation criteria are strict and the task is genuinely ambiguous.

You need explicit loop detection logic. Track how many times a specific agent has been invoked within a single correlation ID. If it crosses a threshold, break the loop, log the full sequence, and either escalate to a human or fail gracefully. An agent that loops indefinitely isn't autonomous; it's stuck.

Context drift and context bloating. This is the subtler problem. When an agent has been running through a complex multi-step task, the context window accumulates information — previous attempts, critic feedback, tool outputs, memory retrievals. Over time, the context gets heavy. Older information that was relevant three steps ago is now noise. The agent starts making decisions based on a bloated context where the signal-to-noise ratio has degraded.

I call this context rot. The context technically has all the information, but the useful information is buried under layers of stale data. The agent's attention drifts to the wrong parts of the context, and the quality of its decisions degrades quietly, no error, no schema violation, just gradually worse outputs that are hard to catch because they're still structurally valid.

The fix is deliberate context management. Trim what's no longer relevant. Keep the context focused on what the current step actually needs. This is an active area of work for me and not a fully solved problem, but the principle is clear: an agent's context should be curated, not accumulated.


Resumability changes everything

Here's a practical problem that most agentic architectures ignore until it's too late.

Say you have a pipeline with fifteen agents. The fifth one fails. In most implementations, you have two options: fix the issue and restart the entire pipeline from scratch, or try to manually hack the state and hope it works.

Both options are bad. Restarting from scratch wastes the work that agents one through four have already completed, work that cost real tokens and real time. Hacking the state is fragile and error-prone.

What you actually need is resumability. The system should checkpoint its state after each agent completes successfully, so that when a failure occurs, the next run picks up from where it left off. Agent one through four already passed? Skip them. Start from agent five with the checkpointed state.

This isn't just about saving costs, although it does, significantly. It's about maintaining a deterministic replay of the workflow. When you can resume from a checkpoint, you can also replay from a checkpoint. That means you can reproduce the failure, test a fix, and run the same sequence again with confidence that everything before the failure point is identical.

Resumability turns debugging from "something broke, restart everything and hope" into "something broke at step five, here's the exact state it started with, here's what it tried, here's why it failed." That's the difference between a prototype and a production system.


Self-healing within limits

I mentioned the critic and falsifier in my previous post. What I didn't get into is what happens when they flag a problem: the system doesn't just report the failure — it feeds the specific feedback back to the original agent and lets it try again.

The critic says, "This result is missing the cost breakdown." The agent gets that feedback, re-examines the task with the specific gap identified, and produces a second attempt. The falsifier says, "This doesn't handle the case where the input is empty." The agent gets that edge case and addresses it.

This self-healing loop is powerful. But it needs hard limits, for a reason that's easy to forget: LLM responses are not deterministic. The same prompt with the same context can produce different outputs across runs. Sometimes the third attempt is better. Sometimes it's worse. Sometimes the agent and the critic get stuck in a disagreement loop where the agent keeps producing valid-but-different responses and the critic keeps finding new things to flag.

You need to find the balance between "rigorous enough to catch real problems" and "flexible enough to accept good-enough results." In my experience, that balance is found through tuning the critic's evaluation criteria, not by adding more retry attempts. A tight critic with two retries beats a loose critic with ten.


What the ideal system looks like

If I were building the perfect agentic debugging and observability system from scratch — and this is genuinely how I think about it — it would be a lightweight, open-source client SDK that integrates with any agentic framework.

It would handle correlation tracking, per-agent tracing, prompt version logging, context window snapshots, tool I/O capture with latency measurement, memory state tracking, schema validation, loop detection, retry management with cost tracking, and resumability from checkpoints.

All of it is emitted in OpenTelemetry format, so you're not locked to any single backend. Plug it into Langfuse for LLM-specific analysis. Plug it into your existing observability stack for infrastructure monitoring. The SDK is the single integration point; the analysis happens wherever your team already works.

This doesn't fully exist today. Pieces of it do. Langfuse handles prompt versioning and cost tracking well, and OpenTelemetry handles distributed tracing. But the agent-specific layer, the thing that understands correlation IDs across agent loops, schema validation failures, context drift, and resumability, is still being built. By me, and I suspect by others who've hit the same walls.


The takeaway

Debugging agentic systems isn't a harder version of debugging regular applications. It's a different discipline. The failure modes are different: loops, context rot, schema violations, memory retrieval errors, tool failures that cascade through retry cycles. The logging requirements are different; you need prompt versions, context snapshots, and decision traces, not just errors and latency. And the recovery model is different — you need resumability and checkpointing, not just restart-and-pray.

If you're building agentic systems and your debugging story is still "we look at the logs and figure it out", you're going to have a rough time in production. Invest in the observability layer early. It's not as exciting as building the agents themselves. But it's what keeps them running at 2 AM when you're not watching.


This is the second in a series on building and operating agentic systems in production. Previously: What "Agentic" Actually Means.

Found this useful? I do 1:1 sessions on AI architecture and strategy. → Book a session

// stay in the loop

If any of this was useful, there's more where that came from.

I write about agentic systems, LLM infrastructure, and what actually works in production — roughly once or twice a month. No noise, no sponsors.