Understanding Max Loop Count Behavior in UI-TARS Desktop: Preventing Premature Termination
The max loop count in UI-TARS Desktop defaults to 100 iterations and halts the agent with a REACH_MAXLOOP_ERROR when exceeded, but you can prevent premature termination by overriding maxLoopCount in the configuration, restricting the MAX_LOOP action space, and properly managing abort signals.
The GUIAgent class in the bytedance/UI-TARS-desktop repository executes a continuous perception-action loop that captures screenshots, queries a vision-language model, and executes predicted actions. To prevent infinite execution, this loop is bounded by a max loop count ceiling that terminates the agent when exceeded. Understanding how this limit works—and the conditions that can trigger early termination—is critical for building stable automation agents.
What Is the Max Loop Count?
The max loop count is a safety mechanism that hard-caps the number of iterations the agent can execute. The default value is defined in packages/ui-tars/shared/src/constants/vlm.ts:
export const MAX_LOOP_COUNT = 100;
When instantiating GUIAgent, you can override this default via the maxLoopCount property in GUIAgentConfig. The constructor extracts this value with a fallback to the constant:
const {
// ...
maxLoopCount = MAX_LOOP_COUNT,
} = this.config;
Source: packages/ui-tars/sdk/src/GUIAgent.ts (lines 76-78)
How the Loop Counter Works
Inside the agent's main execution loop, the counter loopCnt starts at zero and increments once per successful iteration immediately after capturing a screenshot:
loopCnt += 1;
Source: packages/ui-tars/sdk/src/GUIAgent.ts (line 182)
Before processing each iteration, the agent checks if the counter has reached the configured limit:
if (loopCnt >= maxLoopCount) {
Object.assign(data, {
status: StatusEnum.ERROR,
error: this.guiAgentErrorParser(
ErrorStatusEnum.REACH_MAXLOOP_ERROR,
),
});
break;
}
Source: packages/ui-tars/sdk/src/GUIAgent.ts (lines 162-170)
When this condition triggers, the agent immediately exits with a REACH_MAXLOOP_ERROR status.
Causes of Premature Termination
Agents can terminate before hitting the numeric limit due to specific internal actions, external signals, or environmental failures.
The Internal MAX_LOOP Action
The model may deliberately emit the internal action MAX_LOOP, which triggers immediate termination regardless of the counter value:
else if (actionType === INTERNAL_ACTION_SPACES_ENUM.MAX_LOOP) {
Object.assign(data, {
status: StatusEnum.ERROR,
error: this.guiAgentErrorParser(
ErrorStatusEnum.REACH_MAXLOOP_ERROR,
),
});
break;
}
Source: packages/ui-tars/sdk/src/GUIAgent.ts (lines 66-74)
This provides the model a mechanism to signal it cannot complete the task, but it can cause unexpected failures if the model incorrectly predicts this token.
External Stop Signals
The loop monitors several external termination conditions:
if (this.isStopped ||
(data.status !== StatusEnum.RUNNING && data.status !== StatusEnum.PAUSE) ||
signal?.aborted) {
signal?.aborted && (data.status = StatusEnum.USER_STOPPED);
break;
}
Source: packages/ui-tars/sdk/src/GUIAgent.ts (lines 151-160)
These include explicit calls to guiAgent.stop(), status changes to non-running states, or AbortSignal abortion.
Screenshot Failure Exhaustion
Persistent screenshot failures trigger early exit via MAX_SNAPSHOT_ERR_CNT:
if (snapshotErrCnt >= MAX_SNAPSHOT_ERR_CNT) {
// ... error handling and break
}
Source: packages/ui-tars/sdk/src/GUIAgent.ts (lines 172-179)
Preventing Premature Termination
Increase the Loop Ceiling
For long-running tasks, pass a higher maxLoopCount value when constructing the agent:
const agent = new GUIAgent({
operator,
model,
maxLoopCount: 500, // Allow up to 500 iterations
// ... other config
});
This configuration is defined in packages/ui-tars/sdk/src/types.ts as part of the GUIAgentConfig interface.
Restrict Action Spaces
Prevent the model from emitting the MAX_LOOP token by excluding it from your operator's available action spaces:
import { INTERNAL_ACTION_SPACES_ENUM } from '@ui-tars/sdk';
class MyOperator extends Operator {
static MANUAL = {
ACTION_SPACES: [
INTERNAL_ACTION_SPACES_ENUM.CALL_USER,
INTERNAL_ACTION_SPACES_ENUM.FINISHED,
// Deliberately omit INTERNAL_ACTION_SPACES_ENUM.MAX_LOOP
],
};
}
Key definition: packages/ui-tars/sdk/src/constants.ts
Stabilize Abort Handling
Maintain the AbortSignal reference for the duration of the task. Only call controller.abort() or agent.stop() when intentional cancellation is required:
const controller = new AbortController();
const agent = new GUIAgent({
operator,
model,
signal: controller.signal,
});
// To stop intentionally:
controller.abort(); // Sets StatusEnum.USER_STOPPED, not a max-loop error
Handle Snapshot Errors
Provide a stable display environment to prevent intermittent screenshot failures. If occasional failures are expected in your environment, consider adjusting MAX_SNAPSHOT_ERR_CNT if your SDK version exposes this configuration.
Implementation Examples
Configuring a Higher Iteration Limit
import { GUIAgent } from '@ui-tars/sdk';
const agent = new GUIAgent({
operator: myOperator,
model: myModel,
logger: console,
maxLoopCount: 300, // Override default 100
});
await agent.run('Navigate to the settings page and modify preferences');
Filtering the MAX_LOOP Action
import { Operator } from '@ui-tars/sdk';
import { INTERNAL_ACTION_SPACES_ENUM } from '@ui-tars/sdk/constants';
class SafeOperator extends Operator {
static MANUAL = {
ACTION_SPACES: [
INTERNAL_ACTION_SPACES_ENUM.CALL_USER,
INTERNAL_ACTION_SPACES_ENUM.FINISHED,
],
};
// ... operator implementation
}
By not exposing MAX_LOOP in the action space, you eliminate the model's ability to trigger premature termination via that path.
Summary
- The default max loop count is 100 iterations, defined in
packages/ui-tars/shared/src/constants/vlm.tsand enforced inGUIAgent.ts. - The counter increments once per iteration and triggers
REACH_MAXLOOP_ERRORwhen the ceiling is reached. - Premature termination can occur via the internal
MAX_LOOPaction, abort signals, or screenshot failure limits. - Mitigation strategies include overriding
maxLoopCountin the config, removingMAX_LOOPfrom action spaces, and ensuring stable screenshot capture environments.
Frequently Asked Questions
What is the default max loop count in UI-TARS Desktop?
The default limit is 100 iterations, defined as MAX_LOOP_COUNT in packages/ui-tars/shared/src/constants/vlm.ts. This value is used when no override is provided in the GUIAgentConfig.
How do I increase the max loop count limit?
Pass a custom maxLoopCount value when constructing the GUIAgent instance. For example, new GUIAgent({ ..., maxLoopCount: 500 }) allows up to 500 iterations before triggering the error state.
Why does my agent stop with REACH_MAXLOOP_ERROR before completing 100 iterations?
This occurs when the vision-language model emits the internal MAX_LOOP action, or when the agent encounters MAX_SNAPSHOT_ERR_CNT screenshot failures. Check your operator's action spaces configuration to ensure MAX_LOOP is not exposed to the model, and verify display stability to prevent snapshot errors.
Can I disable the max loop count protection entirely?
No, the ceiling cannot be disabled entirely—it is a required safety parameter in the GUIAgent logic. However, you can set an arbitrarily high value (e.g., 999999) via maxLoopCount in the configuration to effectively remove practical limits while maintaining the safety check.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →