Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.tracepilotai.com/llms.txt

Use this file to discover all available pages before exploring further.

OpenAI agent infinite loops are silent budget killers. Your agent retries, re-calls tools, or re-enters its own memory — and nothing in your logs tells you why. npm install tracepilot-openai gives you structured span traces that expose every loop iteration, every tool re-invocation, and every token spent — so you can replay the exact execution that went wrong.

Why OpenAI agent infinite loops happen

Infinite loops in LLM agents aren’t random. They fall into three predictable patterns:

Recursive tool calls

The model calls a tool, receives output it doesn’t know how to use, and calls the same tool again with a slightly different input. Without a hard exit condition, this repeats until you hit a rate limit or your budget runs out.
// Danger: no max_iterations guard
while (true) {
  const response = await openai.chat.completions.create({
    model: 'gpt-4o',
    messages,
    tools,
  });

  const toolCall = response.choices[0].message.tool_calls?.[0];
  if (!toolCall) break; // model never stops calling tools → loop

  const toolResult = await executeTool(toolCall);
  messages.push(toolResultMessage(toolCall.id, toolResult));
}

Retry loops

Error-handling logic retries failed completions unconditionally. If the underlying cause (bad context, token overflow, invalid tool schema) isn’t fixed, every retry triggers the same failure — and the same retry.
// Danger: retry without error classification
async function callWithRetry(messages) {
  try {
    return await openai.chat.completions.create({ model: 'gpt-4o', messages });
  } catch (e) {
    return callWithRetry(messages); // unconditional recursion
  }
}

Memory corruption loops

The agent appends its own output back to the messages array on every iteration. After several turns, the context window fills with redundant or contradictory content. The model starts producing low-quality outputs, which trigger more retries.
// Danger: growing context with no pruning
const memory: Message[] = [];

for (const step of steps) {
  const res = await openai.chat.completions.create({ model: 'gpt-4o', messages: memory });
  memory.push(res.choices[0].message); // unbounded growth
  memory.push({ role: 'user', content: nextStep(res) });
}

Installation

npm
npm install tracepilot-openai
yarn
yarn add tracepilot-openai
pnpm
pnpm add tracepilot-openai

Instrument your OpenAI agent

Wrap every completion call with TracePilot. Each iteration of your agent loop becomes a numbered span — so you can see exactly when and why the loop started repeating.
import OpenAI from 'openai';
import { TracePilotOpenAI } from 'tracepilot-openai';

const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const tp = new TracePilotOpenAI(process.env.TRACEPILOT_API_KEY!);

async function runAgent(userInput: string) {
  await tp.startTrace('tool-calling-agent');

  const messages: any[] = [
    { role: 'user', content: userInput }
  ];

  let stepOrder = 1;
  let parentSpanId: string | undefined;
  const MAX_ITERATIONS = 10; // always set a hard limit

  for (let i = 0; i < MAX_ITERATIONS; i++) {
    const { result, spanId } = await tp.wrapOpenAI(
      () => openai.chat.completions.create({ model: 'gpt-4o', messages, tools }),
      messages,
      parentSpanId,
      stepOrder++
    );

    parentSpanId = spanId;
    const message = result.choices[0].message;
    messages.push(message);

    if (!message.tool_calls?.length) break; // clean exit

    for (const toolCall of message.tool_calls) {
      const { result: toolResult, spanId: toolSpanId } = await tp.wrapToolCall(
        toolCall.function.name,
        () => executeTool(toolCall),
        spanId,
        stepOrder++
      );

      messages.push({
        role: 'tool',
        tool_call_id: toolCall.id,
        content: JSON.stringify(toolResult),
      });

      parentSpanId = toolSpanId;
    }
  }
}

Loop detection with span tracing

With TracePilot active, open your dashboard after a suspicious run. Look for:
SignalWhat it means
Same toolName repeated 3+ timesRecursive tool call loop
Rising token count per span, same promptMemory bloat loop
Identical stepOrder groups repeatingRetry loop
Span count > MAX_ITERATIONSGuard not firing correctly
// Tracing example — detect repeated tool calls
import { TracePilotOpenAI } from 'tracepilot-openai';

const tp = new TracePilotOpenAI(process.env.TRACEPILOT_API_KEY!);

// After your run, the dashboard shows every tool invocation in order.
// Filter by toolName to see repetitions instantly.
// Example dashboard view:
//
// Span 1 — gpt-4o           [320 tokens]
//   Span 2 — search-web     [tool call]
//   Span 3 — gpt-4o         [390 tokens]
//     Span 4 — search-web   [tool call ← repeated!]
//     Span 5 — gpt-4o       [450 tokens]
//       Span 6 — search-web [tool call ← loop confirmed]

How to replay an infinite loop execution

Once you’ve captured the looping trace, you don’t need to reproduce it locally. Fork the span where the loop started and rerun with a modified prompt or context.
1

Find the looping trace

Open tracepilotai.com/dashboard. Filter by high span count or high token usage — loops produce both. Select the trace.
2

Identify the entry point

Expand the span tree. Find the first span where the repeated tool call appears. That’s your entry point for the fix.
3

Fork the span

Click Fork & Rerun on the entry-point span. You’ll see the exact messages array and tool definitions the model received.
4

Apply the fix

Edit the prompt to add an explicit stopping instruction, remove the ambiguous tool, or reduce the context size. Click Run.
5

Confirm the loop is gone

The replayed trace will show a clean exit with fewer spans. Ship the fix when confirmed.
TracePilot loop detection screenshot placeholder

Prevent burning your OpenAI API budget

Every iteration of an infinite loop costs tokens. A 10-step loop on gpt-4o with 1k input tokens per step costs roughly 0.250.25–0.50 per runaway execution. At scale, that’s a real incident. TracePilot gives you three layers of protection:
  1. Span count alerts — set a threshold in the dashboard. Get notified when any trace exceeds N spans.
  2. Cost-per-trace visibility — see the total token spend for every run before it compounds.
  3. Replay without re-execution — fix the bug in the dashboard instead of running the agent again.
// Always instrument cost-sensitive loops
const { result, spanId } = await tp.wrapOpenAI(
  () => openai.chat.completions.create({ model: 'gpt-4o', messages }),
  messages,
  parentSpanId,
  stepOrder
);

// result.usage is captured automatically in the span
// Dashboard shows: prompt_tokens, completion_tokens, estimated_cost
Never run an uninstrumented agent loop in production. Without span tracing, the first sign of a loop is your OpenAI invoice.

Common loop fixes

Add a MAX_ITERATIONS guard and pass { parallel_tool_calls: false } to force sequential tool execution. In the Fork & Rerun view, add an explicit instruction like "If you have already called search-web, do not call it again." to the system prompt.
Classify errors before retrying. Only retry on transient network errors (529, 503). For context-window overflows (400) or invalid tool schemas, fix the root cause — retrying will always fail.
Implement a sliding window: keep only the last N messages plus the system prompt. Use tp.wrapToolCall to trace your memory pruning step so you can inspect what was dropped.
The model re-calls a tool when the output is ambiguous or empty. Use TracePilot to inspect the exact tool output in the span. Add output validation before appending to messages.

Next steps

Quickstart: tracepilot-openai

Get OpenAI agents tracing in under 5 minutes.

Time-travel debugging

Fork any failing span and replay it with edited inputs.

Tracing tool calls

Instrument every tool your agent invokes.

Cost tracking

Monitor token spend per span to catch runaway costs early.