shareAI-lab/learn-claude-code · error · GoalError

max_turns must be at least 1

Error message

max_turns must be at least 1

What it means

AgentSession was constructed with max_turns that is not None and is less than 1 (s17_goal_loop/code.py:541). max_turns bounds the agentic loop (each turn = one model call/tool round); zero or negative turns would forbid even one query, so the constructor rejects it.

Source

Thrown at s17_goal_loop/code.py:541

        },
    },
]


class AgentSession:
    """A small real agent loop with a goal Stop hook at the return boundary."""

    def __init__(
        self,
        client: Any,
        model: str,
        goal: GoalController,
        workdir: Path,
        max_turns: int | None = None,
        background_running: Callable[[], bool] | None = None,
    ):
        if max_turns is not None and max_turns < 1:
            raise GoalError("max_turns must be at least 1")
        self.client = client
        self.model = model
        self.goal = goal
        self.workdir = workdir.resolve()
        self.max_turns = max_turns
        self.background_running = background_running or (lambda: False)
        self.messages: list[dict[str, Any]] = []
        self.total_tokens = 0
        self.hooks: dict[str, list[Callable[..., Any]]] = {
            "UserPromptSubmit": [],
            "PreToolUse": [],
            "PostToolUse": [],
            "Stop": [],
        }
        self.register_hook("PreToolUse", self._permission_hook)
        self.register_hook("PreToolUse", self._log_hook)
        self.register_hook("PostToolUse", self._large_output_hook)
        self.register_hook("UserPromptSubmit", self._context_hook)

View on GitHub (pinned to 985456f4ad)

Solutions

  1. Use max_turns=None for unlimited, or an integer >= 1 for a bounded loop
  2. Validate parsed config/CLI values before constructing the session
  3. Guard arithmetic that derives max_turns from another number

Example fix

# before
session = AgentSession(client, model, goal, workdir, max_turns=0)

# after
session = AgentSession(client, model, goal, workdir, max_turns=None)
Defensive patterns

Strategy: validation

Validate before calling

max_turns = int(os.getenv("MAX_TURNS", 0)) or None  # 0/absent -> unlimited
if max_turns is not None and max_turns < 1:
    raise SystemExit("MAX_TURNS must be >= 1 or unset for unlimited")
session = AgentSession(client, model, goal, workdir, max_turns=max_turns)

Type guard

def is_valid_max_turns(value: object) -> bool:
    return value is None or (isinstance(value, int) and not isinstance(value, bool) and value >= 1)

Prevention

When it happens

Trigger: Instantiating AgentSession(..., max_turns=0) or max_turns=-2. Also passing a CLI/config value like --max-turns 0 that flows into the constructor unvalidated.

Common situations: Interpreting max_turns=0 as 'unlimited' (the library uses None for unlimited); arithmetic on user input (e.g. turns - 1) dipping below 1; config parsers yielding 0 for missing numeric fields.

Related errors


AI-assisted analysis of shareAI-lab/learn-claude-code@985456f4ad (2026-08-14). Data as JSON: /api/errors/3d8d4719b372a259. Report an issue: GitHub.