Debugging Claude Code Agents: Reading Transcripts, Tracing Tool Calls, and Finding Where Your Agent Goes Wrong
Master the techniques for debugging AI agents in production: reading execution transcripts, tracing tool calls, identifying failure patterns, and building custom analyzers that catch problems before users do.
Most agent debugging problems stem from treating AI execution like synchronous code. Developers reach for console.log, step through with a debugger, and wonder why the agent fails in production but works in development. The execution model is fundamentally different: agents make non-deterministic decisions across multiple LLM calls, each influenced by context that changes between runs.
Traditional debugging assumes deterministic behavior. Set a breakpoint, inspect state, reproduce the issue. Agent execution breaks all three assumptions. The same input produces different tool calls. Context windows overflow silently. The model hallucinates field names that don't exist in your schema. By the time the error surfaces, the decision trail that led there is already gone.
flowchart LR
Input("Same user input")
Input --> Console("Console.log outputs")
Console --> NoContext("No context trail")
NoContext --> Silent("Silent failure in prod")
style Silent stroke:#ef4444,fill:#450a0a,color:#fca5a5
The solution requires capturing the complete execution path: every tool call, every model decision, every context state transition. Agents need execution transcripts that show not just what happened, but why the agent chose each action. This distinction is critical. Without the reasoning chain, debugging becomes archaeology—digging through logs to reconstruct decisions that are fundamentally probabilistic.
flowchart LR
Input2("Same user input")
Input2 --> Transcript("Full execution transcript")
Transcript --> Chain("Decision chain captured")
Chain --> Root("Root cause identified")
style Root stroke:#34d399,fill:#0b3b2e,color:#d1fae5
That difference transforms debugging from reactive firefighting to systematic root cause analysis. This post covers the essential patterns: reading Claude Code transcripts, tracing tool execution, identifying common failure modes, and building observability systems that catch issues before they reach production.
Key Takeaways
- Agent debugging requires capturing the complete execution path, not just final outputs—every tool call, reasoning step, and context state must be traced to identify root causes.
- The three most common agent failures are context overflow (exceeds token limits silently), hallucinated fields (model invents schema properties), and reasoning loops (agent retries the same failed approach repeatedly).
- Production observability tools like LangSmith, Arize Phoenix, and Braintrust provide different tradeoffs: LangSmith excels at trace inspection, Phoenix at local development iteration, and Braintrust at evaluation-driven debugging.
- Custom trace analyzers built in TypeScript give teams full control over what signals matter, enabling automated detection of failure patterns specific to their domain.
- Meta-analysis with LLMs can identify patterns across thousands of traces that humans miss, but requires structured prompts that separate symptom description from root cause inference.
Reading Claude Code Transcripts: The Complete Execution Path
Agent transcripts reveal the full decision sequence from user input to final output. Each transcript contains the conversation history, tool calls with their inputs and outputs, and the model's reasoning at each step. Reading these effectively requires understanding what Claude Code captures and what it omits.
The transcript structure follows a linear sequence of turns. Each turn contains a user message or an assistant message with optional tool calls. Tool calls include the function name, arguments, and result. The critical information lives in three places: the assistant's reasoning before calling a tool, the tool arguments that reveal what the model understood, and the tool result that shows whether the execution succeeded.
flowchart TD
Start("User message arrives")
Start --> Parse("Model parses intent")
Parse --> Reason("Reasoning step generated")
Reason --> Tool("Tool call with arguments")
Tool --> Execute("Tool executes")
Execute --> Result("Result returned")
Result --> Next("Next reasoning step")
Next --> End("Final response")
style Tool stroke:#c084fc,fill:#3b0764,color:#f3e8ff,stroke-width:4px
Most debugging failures occur when developers skip the reasoning step. They see a tool call with the wrong arguments and assume the model made a bad decision. The reasoning reveals the actual problem: the model lacked context about valid argument values, or the tool description was ambiguous, or the previous tool result contained misleading information.
Context overflow manifests in transcripts as the model forgetting earlier instructions or tool results. The transcript shows all messages, but Claude Code doesn't indicate when the context window approaches its limit. Developers must calculate token counts manually and watch for symptoms: the model repeating questions it already asked, ignoring tool results from early in the conversation, or making decisions that contradict established context.
The implication here is that transcript length correlates with debugging difficulty. Short conversations with 3-5 tool calls are straightforward to analyze. Conversations with 20+ tool calls require systematic analysis: identify decision points where the execution could have diverged, check whether each tool result influenced the next decision, and verify that critical context remained accessible throughout.
Tracing Tool Calls: Inputs, Outputs, and Where Things Go Wrong
Tool call tracing captures the exact moment when agent execution diverges from expected behavior. The tool name, arguments, and result form a triplet that reveals both what the agent attempted and whether it succeeded. Effective tracing requires structured logging that preserves this triplet across the entire execution.
interface ToolCall {
id: string;
name: string;
arguments: Record<string, unknown>;
result: {
success: boolean;
data?: unknown;
error?: string;
};
timestamp: number;
contextTokens: number;
}
class AgentTracer {
private calls: ToolCall[] = [];
logToolCall(call: ToolCall): void {
this.calls.push(call);
// Detect immediate failure patterns
if (!call.result.success) {
this.analyzeFailure(call);
}
// Detect hallucinated arguments
const schema = this.getToolSchema(call.name);
const invalidArgs = this.findInvalidArguments(call.arguments, schema);
if (invalidArgs.length > 0) {
console.warn(`Hallucinated arguments in ${call.name}:`, invalidArgs);
}
}
private analyzeFailure(call: ToolCall): void {
const recentCalls = this.calls.slice(-5);
const sameToolFailures = recentCalls.filter(
c => c.name === call.name && !c.result.success
);
if (sameToolFailures.length >= 2) {
console.error(`Reasoning loop detected: ${call.name} failed ${sameToolFailures.length} times`);
}
}
private findInvalidArguments(
args: Record<string, unknown>,
schema: Record<string, { type: string; required?: boolean }>
): string[] {
return Object.keys(args).filter(key => !(key in schema));
}
getExecutionSummary(): string {
const total = this.calls.length;
const failed = this.calls.filter(c => !c.result.success).length;
const avgTokens = this.calls.reduce((sum, c) => sum + c.contextTokens, 0) / total;
return `${total} tool calls, ${failed} failures, ${avgTokens.toFixed(0)} avg tokens`;
}
}The tracer captures tool calls as they occur and immediately checks for two common failure modes: the same tool failing repeatedly and arguments that don't exist in the tool schema. Both patterns indicate the agent is stuck and unlikely to recover without intervention.
Tool argument hallucination happens when the model invents field names that seem plausible but don't match the schema. The model sees searchDocuments with a query parameter and assumes requireUnique or maxResults must exist because similar tools have them. The tool execution fails with a validation error, but the model interprets the error as a query problem rather than a schema misunderstanding.
The failure mode here is subtle but expensive. The agent retries with different query values, burning tokens and latency, when the actual fix requires removing the hallucinated field. Detecting this early requires comparing arguments against the known schema before execution and warning when unexpected fields appear.
Common Agent Failure Patterns: Context Overflow, Hallucinated Fields, and Reasoning Loops
Three failure patterns account for most production agent issues: context overflow that causes the model to forget critical information, hallucinated fields that fail validation, and reasoning loops where the agent retries the same broken approach.
Context overflow occurs when the conversation history plus tool results exceeds the model's context window. Claude Code doesn't throw an error when this happens. Instead, older messages get truncated silently. The model continues processing, but without access to earlier context. Decisions that depended on that context become incoherent.
flowchart LR
Start("Context accumulates")
Start --> Limit("Reaches token limit")
Limit --> Truncate("Silent truncation")
Truncate --> Forget("Model forgets context")
Forget --> Wrong("Wrong decision made")
style Truncate stroke:#fbbf24,fill:#3a2f0b,color:#fef3c7
style Wrong stroke:#fbbf24,fill:#3a2f0b,color:#fef3c7
Symptoms appear as inconsistent behavior: the agent asks for information it already received, ignores constraints specified in the initial prompt, or makes decisions that contradict tool results from the beginning of the conversation. Developers see these symptoms and assume the model is unreliable, when the actual problem is mechanical: not enough context capacity.
The fix requires monitoring context tokens throughout execution and implementing a summarization strategy before hitting the limit. When tokens approach 75% of the maximum, summarize earlier messages into a condensed context that preserves critical information. This matters because context overflow is predictable—token counts are deterministic—but invisible without explicit tracking.
Hallucinated fields emerge when tool schemas lack sufficient description or when the model encounters similar tools with different schemas. The model generates plausible-sounding arguments based on patterns it learned during training, but those patterns don't match the actual tool interface.
interface ToolSchema {
name: string;
description: string;
parameters: {
type: "object";
properties: Record<string, {
type: string;
description: string;
enum?: string[];
}>;
required: string[];
};
}
function validateToolCall(
call: { name: string; arguments: Record<string, unknown> },
schema: ToolSchema
): { valid: boolean; errors: string[] } {
const errors: string[] = [];
const validProps = new Set(Object.keys(schema.parameters.properties));
// Check for hallucinated arguments
Object.keys(call.arguments).forEach(arg => {
if (!validProps.has(arg)) {
errors.push(`Unexpected argument '${arg}' not in schema for ${call.name}`);
}
});
// Check for missing required arguments
schema.parameters.required.forEach(req => {
if (!(req in call.arguments)) {
errors.push(`Missing required argument '${req}' in ${call.name}`);
}
});
// Check enum violations
Object.entries(call.arguments).forEach(([key, value]) => {
const prop = schema.parameters.properties[key];
if (prop?.enum && !prop.enum.includes(String(value))) {
errors.push(`Invalid value '${value}' for ${key}, must be one of: ${prop.enum.join(", ")}`);
}
});
return { valid: errors.length === 0, errors };
}Validation before execution prevents hallucinated arguments from reaching the tool. The agent receives immediate feedback about schema violations instead of cryptic execution errors. This reduces debugging time from analyzing error messages to fixing the schema description or constraining argument generation.
Reasoning loops happen when the agent encounters a failure, attempts a retry with minimal changes, fails again, and continues this pattern. Each iteration consumes tokens and latency without making progress toward a solution. The loop continues until context overflow or the user intervenes.
Detection requires tracking tool call patterns across recent history. If the same tool fails three times with similar arguments, the agent is likely stuck. Breaking the loop requires external intervention: inject a system message that explicitly forbids further retries of that tool, or escalate to a human operator who can provide alternative context.
Using LLMs to Debug Agent Traces: Meta-Analysis Patterns
LLMs excel at pattern recognition across large trace volumes. Instead of manually reviewing hundreds of failed agent runs, developers can use a second LLM to analyze the traces and identify common failure modes. This meta-analysis pattern requires structured prompts that separate symptom description from root cause inference.
interface TraceAnalysisPrompt {
systemPrompt: string;
traceContext: {
toolCalls: ToolCall[];
conversationLength: number;
failurePoint: number;
};
analysisType: "failure_root_cause" | "optimization_opportunity" | "pattern_detection";
}
async function analyzeTrace(
trace: ToolCall[],
failureMessage: string
): Promise<{ rootCause: string; recommendation: string }> {
const prompt: TraceAnalysisPrompt = {
systemPrompt: `You are analyzing agent execution traces to identify root causes of failures.
Focus on: context overflow, hallucinated arguments, reasoning loops, and schema mismatches.
Provide specific evidence from the trace, not general observations.`,
traceContext: {
toolCalls: trace,
conversationLength: trace.length,
failurePoint: trace.findIndex(c => !c.result.success)
},
analysisType: "failure_root_cause"
};
const analysis = await callLLM({
model: "claude-3-5-sonnet-20241022",
system: prompt.systemPrompt,
messages: [{
role: "user",
content: `Analyze this agent execution trace that failed with: "${failureMessage}"
Tool calls:
${JSON.stringify(trace, null, 2)}
Identify the root cause and provide an actionable recommendation.`
}]
});
return parseAnalysisResponse(analysis.content);
}The meta-analysis prompt constrains the LLM to focus on known failure patterns rather than generating speculative explanations. The trace context provides concrete evidence: token counts that indicate overflow, argument names that don't match schemas, repeated tool calls that signal loops.
This pattern works best when analyzing batches of similar failures. A single trace might fail for idiosyncratic reasons. Ten traces that fail in the same way reveal a systematic problem: a confusing tool description, insufficient context about valid values, or a missing guard against edge cases.
The limitation is that LLM analysis introduces another layer of non-determinism. The meta-analysis might miss patterns or hallucinate causes that seem plausible but don't match the actual failure. Developers must verify recommendations against the original trace before implementing fixes. This verification step is critical—treating LLM analysis as ground truth leads to debugging dead ends.
Production Observability: LangSmith, Arize Phoenix, and Braintrust for Claude Code
Production observability requires tools that capture traces without degrading agent performance. Three platforms serve different use cases: LangSmith for comprehensive trace inspection, Arize Phoenix for local development iteration, and Braintrust for evaluation-driven debugging.
LangSmith provides the most detailed trace visualization. Each run shows the complete conversation history, tool calls with timing information, and token usage per step. The platform stores traces indefinitely and enables filtering by metadata: user ID, conversation ID, tool names, success/failure status.
flowchart LR
subgraph LangSmith["LangSmith - Full trace history"]
LS1("Agent execution")
LS1 --> LS2("Auto-captured traces")
LS2 --> LS3("Web UI inspection")
end
subgraph Phoenix["Phoenix - Local iteration"]
PX1("Development agent")
PX1 --> PX2("Localhost traces")
PX2 --> PX3("Immediate feedback")
end
subgraph Braintrust["Braintrust - Evaluation focus"]
BT1("Production logs")
BT1 --> BT2("Eval datasets")
BT2 --> BT3("Regression detection")
end
style LS3 stroke:#34d399,fill:#0b3b2e,color:#d1fae5
style PX3 stroke:#34d399,fill:#0b3b2e,color:#d1fae5
style BT3 stroke:#34d399,fill:#0b3b2e,color:#d1fae5
The strength lies in post-mortem analysis. When a user reports an issue, developers query LangSmith by conversation ID and see exactly what the agent did. The trace shows whether the failure was a tool error, a context problem, or incorrect reasoning. This visibility cuts debugging time from hours to minutes.
Arize Phoenix targets local development. The platform runs as a localhost service that captures traces from development agents. Developers iterate on prompts or tool schemas and immediately see how changes affect trace quality. The feedback loop is tight: modify a tool description, run a test conversation, inspect the trace, repeat.
Phoenix excels when building new agent capabilities. The local-first approach means no network latency for trace upload and no concerns about exposing development traces to external services. The tradeoff is that Phoenix stores traces in memory—restart the service and history disappears. This works for development but not for production monitoring.
Braintrust focuses on evaluation-driven debugging. The platform treats traces as evaluation inputs. Developers create test datasets from production failures, run evaluations that replay those scenarios, and track whether code changes improve success rates. The workflow surfaces regressions: if a prompt change fixes one scenario but breaks two others, the evaluation fails.
This matters because agent changes often have non-local effects. Improving tool descriptions for one use case might confuse the model in different contexts. Evaluation-based workflows catch these regressions before deployment. The investment in building evaluation datasets pays off in reduced production incidents.
The choice depends on team workflow. Teams doing rapid prototyping benefit from Phoenix's tight iteration loop. Teams with established agents and production traffic need LangSmith's trace retention. Teams practicing test-driven agent development should use Braintrust's evaluation framework. Many teams use all three: Phoenix in development, Braintrust in CI, LangSmith in production.
Building Custom Trace Analyzers in TypeScript
Custom analyzers provide control over what signals matter for a specific domain. General-purpose observability platforms capture everything. Custom analyzers surface patterns that matter to your business: compliance violations, cost anomalies, user experience degradation.
interface AnalysisRule {
name: string;
check: (trace: ToolCall[]) => { triggered: boolean; severity: "low" | "medium" | "high"; details: string };
}
class CustomTraceAnalyzer {
private rules: AnalysisRule[] = [];
addRule(rule: AnalysisRule): void {
this.rules.push(rule);
}
analyze(trace: ToolCall[]): { violations: Array<{ rule: string; severity: string; details: string }> } {
const violations = this.rules
.map(rule => {
const result = rule.check(trace);
return result.triggered ? { rule: rule.name, severity: result.severity, details: result.details } : null;
})
.filter((v): v is NonNullable<typeof v> => v !== null);
return { violations };
}
}
// Example: Detect excessive API calls to expensive services
const costControl: AnalysisRule = {
name: "excessive_expensive_api_calls",
check: (trace) => {
const expensiveTools = ["searchDatabase", "generateReport"];
const calls = trace.filter(c => expensiveTools.includes(c.name));
if (calls.length > 5) {
const cost = calls.length * 0.25; // $0.25 per call
return {
triggered: true,
severity: "high",
details: `Made ${calls.length} expensive API calls (estimated $${cost.toFixed(2)})`
};
}
return { triggered: false, severity: "low", details: "" };
}
};
// Example: Detect potential PII exposure
const piiExposure: AnalysisRule = {
name: "pii_in_tool_arguments",
check: (trace) => {
const piiPattern = /\b\d{3}-\d{2}-\d{4}\b|\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/i;
for (const call of trace) {
const argsString = JSON.stringify(call.arguments);
if (piiPattern.test(argsString)) {
return {
triggered: true,
severity: "high",
details: `Potential PII found in ${call.name} arguments`
};
}
}
return { triggered: false, severity: "low", details: "" };
}
};
// Usage
const analyzer = new CustomTraceAnalyzer();
analyzer.addRule(costControl);
analyzer.addRule(piiExposure);
const traceToAnalyze: ToolCall[] = [
{ id: "1", name: "searchDatabase", arguments: { query: "user@example.com" }, result: { success: true }, timestamp: Date.now(), contextTokens: 1500 }
];
const analysis = analyzer.analyze(traceToAnalyze);
console.log(analysis.violations);
// Output: [{ rule: "pii_in_tool_arguments", severity: "high", details: "Potential PII found in searchDatabase arguments" }]The analyzer runs rules against each trace and returns violations. Each rule encapsulates domain knowledge: what constitutes excessive API usage, which data patterns indicate compliance risk, what timing thresholds signal user experience problems.
This pattern integrates with existing observability platforms. Traces captured by LangSmith or Phoenix feed into the custom analyzer, which applies business-specific rules and surfaces violations. The separation of concerns works: platforms handle trace capture and storage, analyzers handle domain-specific detection.
The extensibility here enables rapid response to new failure modes. When a production incident reveals a pattern that general tools missed, developers write a new rule and deploy it immediately. The rule runs against historical traces to detect whether the problem occurred before. This retroactive analysis often reveals that a "new" issue has been happening for weeks.
Frequently Asked Questions
How do I know if context overflow caused an agent failure?
Calculate total tokens across all messages and tool results in the conversation history—if the sum approaches 200K tokens (Claude's limit) and the agent starts contradicting earlier decisions or forgetting tool results, context overflow is the likely cause. Implement token tracking at each turn and set alerts at 75% of capacity.
What's the difference between hallucinated fields and schema validation errors?
Hallucinated fields occur when the model invents argument names that don't exist in the tool schema (like adding requireUnique to a search function). Schema validation errors happen when the model uses correct field names but provides values of the wrong type or outside allowed ranges—both fail validation, but hallucinations indicate the model didn't understand the available arguments.
Can I use Claude to debug Claude agent traces?
Yes, meta-analysis with LLMs works well for pattern detection across multiple traces, but requires structured prompts that constrain the analysis to known failure modes (context overflow, loops, hallucinations) and always verify recommendations against the original trace data before implementing fixes.
Should I build custom analyzers or use observability platforms?
Start with platforms like LangSmith for trace capture and basic inspection, then add custom analyzers when you identify patterns specific to your domain (cost thresholds, compliance rules, business logic violations) that general tools don't detect—most production setups use both.
How do I detect reasoning loops before they burn through my token budget?
Track the last 3-5 tool calls in memory and check if the same tool name appears with failed results more than twice—if detected, inject a system message forbidding further retries of that tool or escalate to human intervention, as loops rarely self-correct and will consume tokens until context overflow occurs.
Conclusion: From Reactive Debugging to Proactive Agent Health Monitoring
Agent debugging succeeds when teams shift from investigating individual failures to monitoring execution health across all runs. Reactive debugging answers "why did this specific run fail?" Proactive monitoring answers "what patterns predict failure before users encounter them?"
The patterns covered here form a debugging stack: transcript inspection for understanding individual failures, tool call tracing for capturing the decision sequence, failure pattern detection for systematic issues, meta-analysis for cross-run insights, observability platforms for production visibility, and custom analyzers for domain-specific rules. Each layer builds on the previous one.
Production teams that implement this stack report two outcomes: fewer user-reported incidents and faster resolution when issues do occur. Fewer incidents because monitoring catches problems before they affect users. Faster resolution because traces provide the complete context needed for root cause analysis. Both outcomes matter more than the upfront investment in observability infrastructure.
That covers the essential patterns for debugging Claude Code agents. Apply these in production and the difference will be immediate: from opaque failures to traceable execution paths, from guesswork to evidence-based fixes, from reactive firefighting to predictive health monitoring.