Skip to content

max_turns check compares num_turns (one per tool result) against --max-turns (API rounds) — false failures with parallel tool calls #1795

Description

@Tsogt-hub

Since #1607 (fix: enforce max turns from claude args, follow-up to #1577), base-action/src/run-claude-sdk.ts throws when resultMessage.num_turns > sdkOptions.maxTurns, even though subtype === "success" and is_error === false:

Claude reported a successful result after 108 turns, exceeding the configured maximum of 80

The two numbers measure different things, so the check produces false failures on runs that completed normally:

  • --max-turns N (CLI) caps the number of agentic API rounds. Claude Code 2.1.259 enforces it (subtype: "error_max_turns" when reached).
  • num_turns in the result message counts one per user-role message, i.e. the initial prompt plus one per tool_result. With parallel tool calls, one API round yields several tool results, so num_turns grows faster than rounds.

Reproduction (Claude Code 2.1.259, local):

claude -p "Run six separate echo commands, one tool call each: echo 1 … echo 6, then say done" \
  --max-turns 3 --output-format json --allowedTools "Bash(echo:*)"

Result: the CLI stops after 3 rounds with subtype: "error_max_turns" and reports num_turns: 4 (3 tool results + prompt). The cap works; num_turns is simply not the number of rounds.

In CI (this action, claude_args: --max-turns 80): the execution artifact of one run shows 36 distinct assistant message.ids (rounds) but 107 tool calls → num_turns: 108 → the action failed with the message above, although the PR and the result comment had already been created. With a model that issues ~3 tool calls per round, most non-trivial runs trip the check; 14 of our 15 failed runs in two days were of this kind (is_error: false, subtype: "success").

I believe #1577 observed the same effect (--max-turns 60num_turns: 73, subtype: "success") and read it as the CLI failing open; the post-hoc check from #1607 then turned that observation into a hard failure.

Suggested fix: compare against the number of assistant rounds (distinct assistant message.id in the SDK stream), or drop the post-hoc check since the CLI already enforces --max-turns and reports error_max_turns.

Workaround we use: --max-turns 240 (≈ 80 rounds × ~3 tool calls) with the job's timeout-minutes as the hard budget.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingp2Non-showstopper bug or popular feature request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions