we-promise/sure · error · Assistant::Responder::ToolCallLimitError
Assistant exceeded the tool-call limit of #{max_tool_call_it
Error message
Assistant exceeded the tool-call limit of #{max_tool_call_iterations} for one response What it means
Raised by Assistant::Responder#respond as ToolCallLimitError when the LLM keeps requesting function calls for more than max_tool_call_iterations round trips within a single user turn. The counter increments once per loop iteration (one LLM response with function_requests → tool execution → next LLM response); the default limit is 5, overridable via ENV['ASSISTANT_MAX_TOOL_CALL_ITERATIONS']. It is a runaway-loop guard so one turn cannot ping-pong with tools forever.
Source
Thrown at app/models/assistant/responder.rb:26
@instructions = instructions
@function_tool_caller = function_tool_caller
@llm = llm
end
def on(event_name, &block)
listeners[event_name.to_sym] << block
end
def respond(previous_response_id: nil)
response, response_has_text = request_response(previous_response_id: previous_response_id)
any_response_has_text = response_has_text
in_flight_function_results = []
iteration = 0
while response.function_requests.any?
iteration += 1
if iteration > max_tool_call_iterations
raise ToolCallLimitError,
"Assistant exceeded the tool-call limit of #{max_tool_call_iterations} for one response"
end
function_tool_calls = function_tool_caller.fulfill_requests(response.function_requests)
function_results = function_tool_calls.map(&:to_result)
in_flight_function_results.concat(function_results)
emit(:response, {
id: response.id,
function_tool_calls: function_tool_calls
})
response, response_has_text = request_response(
function_results: provider_preserves_response_context? ? function_results : in_flight_function_results.dup,
previous_response_id: response.id
)
any_response_has_text ||= response_has_text
endView on GitHub (pinned to e69894adb9)
Solutions
- Raise the limit via ENV when the workflow legitimately needs more round trips: ASSISTANT_MAX_TOOL_CALL_ITERATIONS=10 (invalid or non-positive values fall back to 5).
- Inspect the emitted :response events to see which tool the model loops on, then fix that tool's output — usually an error result it cannot recover from.
- Tighten instructions so the model must answer after gathering data, and encourage batching (update_budget accepts multiple categories in one call).
- Rescue Assistant::Responder::ToolCallLimitError in the caller and surface a friendly message instead of a raw 500.
Example fix
# before responder.respond # after — bound the turn and degrade gracefully begin responder.respond rescue Assistant::Responder::ToolCallLimitError message.update!(content: "I hit my tool-use limit trying to answer that. Could you narrow the request?") end # and/or, in env, when the workflow legitimately needs more hops: # ASSISTANT_MAX_TOOL_CALL_ITERATIONS=10
Defensive patterns
Strategy: try-catch
Validate before calling
limit = Integer(ENV.fetch("ASSISTANT_MAX_TOOL_CALL_ITERATIONS", 5))
raise ArgumentError, "ASSISTANT_MAX_TOOL_CALL_ITERATIONS must cover this workflow" if workflow_needs_more_hops_than?(limit) Try / catch
begin responder.respond(previous_response_id: prev_id) rescue Assistant::Responder::ToolCallLimitError # surface partial progress; the turn's tool calls were already emitted via :response events message.update!(content: "I hit my tool-use limit — please narrow the request or continue in a new message.") end
Prevention
- Set ASSISTANT_MAX_TOOL_CALL_ITERATIONS to cover your longest legitimate tool chain (invalid values silently fall back to 5).
- Instrument which tool the model loops on via the :response events; fix error-result tool outputs the model keeps retrying.
- Design tools to batch (e.g. update_budget accepts a categories array) so one turn needs fewer round trips.
When it happens
Trigger: The while loop at app/models/assistant/responder.rb:23-44 runs a 6th iteration: e.g. the model alternates get_budget → update_budget → get_budget → ... never emitting text; or a tool keeps returning an error payload the model keeps retrying; or the task legitimately needs more than 5 sequential tool round trips (default).
Common situations: Broad prompts requiring many sequential lookups (scan every account, then every month); tool results that are error hashes the model retries instead of giving up; instructions that encourage verification loops after each write; ASSISTANT_MAX_TOOL_CALL_ITERATIONS lowered or left at default while workflows grew.
Related errors
- Assistant returned neither text nor tool calls
- No LLM provider configured that supports model '#{requested_
- Invalid month: #{raw}. Use YYYY-MM or MMM-YYYY.
- #{label} must be a non-negative number.
- Each categories entry needs a category name or id.
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/745edbc21b1eee2b.
Report an issue: GitHub.