Agent loop stop conditions are the rules that decide when an autonomous tool-using agent should stop iterating and return a result. In the AI SDK loop-control model, execution continues until one of a few events occurs: the model stops producing tool calls, a tool without an execute function is called, a tool call needs approval, or a stop condition is met.

The practical goal is simple: let the agent do enough work to be useful, but prevent runaway loops, excessive API calls, and uncontrolled cost. The default safety behavior is a 20-step limit via isStepCount(20), and you can replace or combine that default with your own conditions depending on the workflow.

What a stop condition does

A stop condition is evaluated during the agent loop to decide whether the loop should continue after tool use. In the AI SDK, this is handled with the stopWhen parameter.

Use a stop condition when you need the loop to end based on:

  • step count
  • a specific tool call
  • natural completion
  • custom business logic
  • cost or token budget

The key design choice is whether the agent should stop because it has reached a structural limit, or because it has satisfied a semantic requirement.

Built-in stop conditions

The reference material describes three built-in conditions:

  • isStepCount(count) — stop after a specified number of steps
  • hasToolCall(...toolNames) — stop when any specified tool is called
  • isLoopFinished() — never triggers on its own, allowing the loop to continue until the agent is naturally finished

1) isStepCount(count)

This is the safest general-purpose guardrail. It caps loop length and prevents unbounded execution.

Use it when:

  • you want predictable cost
  • the task may involve several tool calls
  • you need a hard upper bound on work

Example:

import { ToolLoopAgent, isStepCount } from 'ai';

const agent = new ToolLoopAgent({
  model: "xai/grok-4.5",
  tools: {
    // your tools
  },
  stopWhen: isStepCount(50),
});

const result = await agent.generate({
  prompt: 'Analyze this dataset and create a summary report',
});

Why it matters:

  • It provides a clear maximum.
  • It works even if the model keeps trying to use tools.
  • It is easy to reason about in production.

When not to use it alone:

  • When the task can naturally take variable time and you need a more semantic stop rule.
  • When the agent should stop on a specific completion signal rather than a fixed count.

2) hasToolCall(...toolNames)

This condition stops when the agent invokes one of the named tools.

Use it when:

  • a specific tool marks completion
  • you want to stop once a terminal action has been requested
  • you are using a done-style pattern to signal completion

Example pattern:

import { ToolLoopAgent, isStepCount, hasToolCall } from 'ai';

const agent = new ToolLoopAgent({
  model: "xai/grok-4.5",
  tools: {
    // your tools
  },
  stopWhen: [
    isStepCount(20),
    hasToolCall('someTool', 'done'),
  ],
});

This is useful when a named tool call is the meaningful end state.

3) isLoopFinished()

This tells the agent to keep going until the model naturally stops producing tool calls.

Use it when:

  • you want the model to determine completion
  • you are comfortable with longer-running tool sequences
  • you also have external protections in place

Important trade-off:

  • It removes the default step limit.
  • Without another boundary, the agent could run indefinitely or accumulate significant cost.

Use it carefully in production. In most systems, this should be paired with another guardrail such as a budget cap or an operational timeout.

How to combine stop conditions

The stopWhen parameter accepts an array. When any condition is met, the loop stops.

This is usually the best production pattern because it lets you combine:

  • a hard safety cap
  • a semantic terminal condition
  • possibly a budget or policy rule

Example:

import { ToolLoopAgent, isStepCount, hasToolCall } from 'ai';

const agent = new ToolLoopAgent({
  model: "xai/grok-4.5",
  tools: {
    // your tools
  },
  stopWhen: [
    isStepCount(20),
    hasToolCall('someTool', 'done'),
  ],
});
Goal Good stop condition
Prevent runaway loops isStepCount(n)
Stop when a terminal tool is called hasToolCall('done')
Let the model finish naturally, but keep a safety backstop isLoopFinished() plus a separate step limit in your own logic
Stop on custom content Custom StopCondition
Control costs Custom budget condition plus a step cap

Custom stop conditions

Custom stop conditions are the most flexible option. The reference shows StopCondition functions that inspect accumulated steps and return true when the loop should end.

Use custom conditions when you need to stop based on:

  • text content
  • tool history
  • usage totals
  • budget estimates
  • workflow-specific completion rules

Example: stop when the agent generates text containing ANSWER:.

import { ToolLoopAgent, StopCondition, ToolSet } from 'ai';

const tools = {
  // your tools
} satisfies ToolSet;

const hasAnswer: StopCondition<typeof tools> = ({ steps }) => {
  return steps.some(step => step.text?.includes('ANSWER:')) ?? false;
};

const agent = new ToolLoopAgent({
  model: "xai/grok-4.5",
  tools,
  stopWhen: hasAnswer,
});

Example: stop when estimated cost exceeds a threshold.

const budgetExceeded: StopCondition<typeof tools> = ({ steps }) => {
  const totalUsage = steps.reduce(
    (acc, step) => ({
      inputTokens: acc.inputTokens + (step.usage?.inputTokens ?? 0),
      outputTokens: acc.outputTokens + (step.usage?.outputTokens ?? 0),
    }),
    { inputTokens: 0, outputTokens: 0 },
  );

  const costEstimate =
    (totalUsage.inputTokens * 0.01 + totalUsage.outputTokens * 0.03) / 1000;

  return costEstimate > 0.5;
};

When custom stop conditions are the right choice

Use them when:

  • a simple step count is too blunt
  • the agent needs to stop on domain-specific criteria
  • you want policy-aware or budget-aware termination

Trade-offs

Custom logic is more expressive, but it also:

  • adds implementation complexity
  • can be harder to test
  • may depend on imperfect heuristics such as token estimation

For that reason, custom conditions are usually best when combined with a hard step cap.

prepareStep is not a stop condition, but it affects termination

The prepareStep callback runs before each loop step. It does not directly stop the loop, but it can change the model, tools, and message state in ways that influence whether the loop finishes.

Use prepareStep for:

  • dynamic model selection
  • context compaction
  • changing tool availability
  • forcing a tool choice
  • modifying the sandbox for a step

Why this matters for stop behavior

A loop can fail to end not because the stop condition is wrong, but because the agent keeps receiving the wrong context or tool set. prepareStep helps solve that by steering the loop toward completion.

Examples from the reference include:

  • switching to a stronger model after the first few steps
  • pruning messages when they become too large
  • limiting tools to the current phase of work
  • forcing a specific tool choice at a specific step

Context management example

import { ToolLoopAgent, pruneMessages, type ModelMessage } from 'ai';

const COMPACTION_THRESHOLD = 100_000;

const estimateTokens = (messages: ModelMessage[]) => {
  return JSON.stringify(messages).length / 4;
};

const agent = new ToolLoopAgent({
  model: "xai/grok-4.5",
  tools: {
    // your tools
  },
  prepareStep: async ({ messages }) => {
    if (estimateTokens(messages) > COMPACTION_THRESHOLD) {
      return {
        messages: pruneMessages({
          messages,
          reasoning: 'all',
          toolCalls: 'before-last-3-messages',
          emptyMessages: 'remove',
        }),
      };
    }
  },
});

This is not a stop rule by itself, but it can reduce the chance of endless loops caused by bloated context.

A practical decision guide

Use isStepCount when:

  • you need a safe default
  • the loop should never run beyond a known maximum
  • you are building a production system with cost limits

Use hasToolCall when:

  • a terminal tool marks completion
  • the workflow uses explicit completion signaling
  • you want to stop immediately after a known event

Use isLoopFinished when:

  • the agent should control its own finish state
  • you can tolerate variable runtime
  • you still have operational protections elsewhere

Use a custom StopCondition when:

  • stopping depends on domain logic
  • you need budget awareness
  • completion is encoded in output text or tool history

Use multiple conditions when:

  • you want both safety and semantic correctness
  • you need a fail-safe cap plus a meaningful end state

Common failure modes

1) Infinite or near-infinite loops

Symptoms:

  • repeated tool calls
  • no final answer
  • escalating cost

Prevention:

  • add isStepCount(n)
  • avoid using isLoopFinished() without another guardrail
  • inspect whether tool outputs are giving the model enough signal to finish

2) Premature stopping

Symptoms:

  • agent stops before useful work is done
  • incomplete analysis
  • missing final synthesis

Prevention:

  • increase the step limit
  • make the terminal tool or condition more specific
  • ensure your stop condition matches the actual completion signal

3) Tool-churn without progress

Symptoms:

  • the agent keeps calling tools but never converges

Prevention:

  • use prepareStep to limit tool availability by phase
  • compact messages when context grows too large
  • switch models when reasoning becomes more complex

4) Cost surprises

Symptoms:

  • the loop technically works but usage is too high

Prevention:

  • combine isStepCount with a custom budget condition
  • track usage across steps
  • set explicit operational limits

For most agent systems, the safest setup is:

  1. A hard maximum step count
  2. A semantic terminal condition
  3. Optional budget tracking
  4. Context compaction through prepareStep

That combination gives you:

  • predictable termination
  • meaningful completion
  • protection against runaway usage
  • better behavior on long-running tasks

A common pattern is:

stopWhen: [
  isStepCount(20),
  hasToolCall('done'),
]

Then, if your workflow needs more nuance, add a custom budget rule.

Forced tool calling and completion signaling

The reference also describes a pattern where toolChoice: 'required' is paired with a done tool that has no execute function. In that setup, the agent is forced to use tools each step, and the loop stops when the agent calls done.

This is useful when you want every step to be structured and machine-checkable.

It is less suitable when:

  • you want the model to answer directly
  • tool use is optional
  • the workflow needs faster termination with fewer enforced steps

Manual loop control

If you need complete control, the reference suggests building your own loop with generateText or streamText rather than relying only on stopWhen and prepareStep.

This is appropriate when you need:

  • explicit message history management
  • custom recovery logic
  • step-by-step orchestration
  • dynamic tool and model selection outside the built-in loop

Manual control gives you flexibility, but it also shifts responsibility for stopping, retries, and state management onto your code.

Bottom line

The best agent loop stop condition is usually not a single rule. It is a layered design:

  • a hard cap to prevent runaway loops
  • a semantic condition to recognize completion
  • optional budget or policy logic
  • message and tool management to keep the loop converging

If you only remember one thing, remember this: never rely on “the model will know when to stop” without a backup limit.

Sources

Frequently Asked Questions

What is the safest default stop condition?

isStepCount(20) is the safest default because it provides a hard upper bound on loop length.

When should I use isLoopFinished()?

Use it only when you want the loop to continue until the model naturally stops making tool calls, and you have other safeguards in place.

Can I combine multiple stop conditions?

Yes. When stopWhen is an array, the loop stops when any condition is met.

Is prepareStep a stop condition?

No. It changes settings or message state before each step, which can influence termination indirectly, but it does not itself stop the loop.

Source record

  1. ai-sdk.dev
  2. www.youtube.com
  3. www.mindstudio.ai
  4. medium.com
  5. dev.to
  6. strandsagents.com
  7. www.mindstudio.ai
  8. medium.com