Skip to content

performAgentChain has no iteration cap — infinite loop on repeating tool calls #175

@Vladimir44343

Description

@Vladimir44343

Bug Description

The main performAgentChain loop in backend/pkg/providers/performer.go is an unbounded for {} loop with no maximum iteration counter. When a model repeatedly calls the same tool (e.g., due to model limitations), the repeating detector fires and returns "tool call 'X' is repeating, please try another tool" as a successful response (nil error). This means wantToStop is never set to true, and the loop continues indefinitely.

Reproduction

  1. Use a smaller/local model (e.g., Qwen3-Coder-Next-FP8 via vLLM) as the LLM provider
  2. Start a pentest flow that triggers terminal commands
  3. When the model encounters an error from a terminal command, it may retry the same command
  4. After 3 identical calls, the repeating detector fires
  5. The model receives "tool call 'X' is repeating, please try another tool" but ignores it and calls the same tool again
  6. This creates an infinite loop — we observed 4,800+ repeating warnings in a single session

Root Cause

In performer.go, execToolCall returns (response, nil) when repeating is detected:

if detector.detect(toolCall) {
    response := fmt.Sprintf("tool call '%s' is repeating, please try another tool", funcName)
    return response, nil  // <-- nil error means loop continues
}

The performAgentChain loop has no iteration cap:

for {  // no max iterations
    result, err := fp.callWithRetries(ctx, chain, ...)
    // ...
    if wantToStop { return nil }
    // loop continues forever
}

Suggested Fix

Add a hard iteration cap to performAgentChain:

const maxAgentChainIterations = 100

for iteration := 0; ; iteration++ {
    if iteration >= maxAgentChainIterations {
        return fmt.Errorf("agent chain exceeded maximum iterations (%d)", maxAgentChainIterations)
    }
    // ... existing loop body
}

Additionally, consider adding escalation to the repeating detector — after N consecutive repeating detections (e.g., 5), return an actual error instead of a message:

if detector.repeatCount >= maxRepeatingBeforeAbort {
    return "", fmt.Errorf("tool '%s' repeated %d times, aborting chain", funcName, detector.repeatCount)
}

Environment

  • PentAGI v1.2.0 (Docker image built 2026-02-25)
  • LLM: Qwen3-Coder-Next-FP8 via vLLM 0.15.1 (custom provider)
  • 2x NVIDIA GB10 (DGX Spark)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions