Max iterations (10) reached. The agent could not complete the task within the allowed number of iterations.
Of the frameworks on this site, n8n is the one that fails the way you would want: it throws, the node goes red, and nothing downstream runs on a guess. But the sentence above is only produced by the newer of two execution paths inside the same node. The older path hands the same ceiling to LangChain, which does not throw and returns a sentence instead — so the string you got tells you which path you are on, and that is the first thing to establish before you touch the setting.
One file, one function, and it does nothing else:
export function checkMaxIterations( response: EngineResponse<RequestResponseMetadata> | undefined, maxIterations: number, node: INode, ): void { // Only check if this is a continuation (response has iteration count) if (response?.metadata?.iterationCount === undefined) { return; } if (response.metadata.iterationCount >= maxIterations) { throw new NodeOperationError( node, `Max iterations (${maxIterations}) reached. The agent could not complete the task within the allowed number of iterations.`, ); } }
The whole file, verbatim, from packages/@n8n/nodes-langchain/nodes/agents/Agent/agents/ToolsAgent/V3/helpers/checkMaxIterations.ts at commit ab2c8b56. The number in your message is interpolated, so a node left on the default reads “(10)”.
Two details in those fourteen lines are worth more than the message itself.
It is a NodeOperationError, and it carries the node. That is why this failure shows up attached to a specific node on the canvas rather than as a bare workflow error. It is thrown, not returned: the node fails, and what happens next is whatever your workflow is configured to do with a failing node.
It only fires on a continuation. The early return is explicit — if the engine response carries no iterationCount, the function does nothing. The count lives on the response from the previous engine round, so the ceiling is enforced when the agent comes back for another round of tool calls, not on the first pass.
The call site is in the batch executor, and the default is visible twice:
// Check max iterations if this is a continuation of a previous execution const maxIterations = ctx.getNodeParameter('options.maxIterations', 0, 10); assertParamIsNumber('options.maxIterations', maxIterations, ctx.getNode()); const batchPromises = batch.map(async (_item, batchItemIndex) => { const itemIndex = startIndex + batchItemIndex; checkMaxIterations(response, maxIterations, ctx.getNode());
From V3/helpers/executeBatch.ts at commit bea96e54 — the 10 is the fallback in the parameter read. The same default is declared on the option itself in ToolsAgent/options.ts at commit 64c337bb: displayName: 'Max Iterations', default: 10, described as “The maximum number of iterations the agent will run before stopping”. Note also where the check sits — inside the per-item map, so it is evaluated once per item in the batch.
You do not need to read your logs for this one. You need to know which of two endings you got.
Both older paths pass the option straight through: maxIterations: options.maxIterations ?? 10 into AgentExecutor.fromAgentAndTools({...}), in ToolsAgent/V2/execute.ts and the V1 file beside it, read from master on 2 October 2026. That executor is the TypeScript one, which matters for the string: in libs/langchain-classic/src/agents/agent.ts at commit b261746b, returnStoppedResponse with the default earlyStoppingMethod of "force" returns { output: "Agent stopped due to max iterations." }, and that is the method both stop paths in executor.ts call. Its default maxIterations is 15, so on these node versions the effective ceiling is n8n’s 10, not LangChain’s. The green-node case is the one to worry about, for the same reason it is on the other pages here: nothing failed, so nothing retried.
An iteration here is a round of tool calls — one model call and the tool calls it asked for. Ten of them is not many for an agent that is working through a list, which is why this is the n8n failure people hit first. Raising it to 40 buys 30 more rounds, and if the job genuinely needed 14, that is the right fix and you are done.
What the bigger number does not change is what the run leaves behind. The check is a ceiling on a loop that has no other reason to stop; raising it moves the moment the node goes red and nothing else. An agent that cannot finish in ten rounds because it is walking a list one item at a time will not finish in forty either — it will spend four times the tokens arriving at the same red node, and the per-item structure that caused it is still there. The setting is worth raising once, deliberately, with a number you can defend. Raising it twice is a signal to change the shape of the work.
n8n has an advantage the code-first frameworks on this site do not: the unit of work is a node, and node outputs are materialised in the run. Use that.
Give the agent one tool that does the whole list. This is the change that actually removes the failure. Most agents that hit ten rounds are doing one item per round because every tool they were given takes one item. A tool that accepts the whole array turns ten rounds into one, and the iteration count stops tracking the item count. In n8n that is usually a sub-workflow tool over the full input rather than a per-item HTTP call.
Put the loop outside the agent. If the work really is per-item, iterate in the workflow and let the agent handle one item per execution. Then the iteration ceiling applies to one item’s worth of reasoning, the items that already ran have already written their outputs, and a failure on item seven is a failure on item seven rather than on the batch.
Decide, explicitly, what a failing node should do. Because this path throws rather than returning, the fate of the finished work is governed by the node’s own error settings and by where you put the loop — not by the agent. That is a setting on your node and we have not read n8n’s error-handling source, so check it in your own instance rather than taking it from this page: whatever it is set to is what decides whether a capped run keeps nine items or none.
There is a written guide: the iteration arithmetic as a formula you can run against a job before you launch it, the reasons raising a cap does not finish the job, and batch.py, one standard-library file that collapses a per-item loop into a single pass — which is the code-first version of the one change this page says is worth making.
No page on this site has a checkout widget of its own. There is a written guide behind this host and it is on sale at $19 on a storefront that delivers the files automatically and carries a 30-day money-back guarantee: buy it there (checked 2 October 2026); the guide can also be paid for with 19 USDC on Base at the payment page, where delivery is by hand as a reply to your email. Every page on this site, including this one, is free to read in full, with no sign-up and nothing gated.
The short version: Max iterations (10) reached. is a NodeOperationError thrown by the newer tools-agent path, only on a continuation, with the ceiling read from the node’s Max Iterations option whose default is ten. Older node versions hand the same ceiling to the TypeScript LangChain executor, which returns Agent stopped due to max iterations. as the output instead of throwing — so check whether your node went red or green before you change anything. Then give the agent a tool that takes the whole list, and put the per-item loop in the workflow where its outputs survive.
This page counts anonymous readership. Each load sends the page path, the address of the page you came from (our server keeps only its domain), how long the page was open, whether you scrolled, and any campaign or outreach code in the link you followed; a second “engaged” event is sent once, ten seconds after the page opens — whether or not the tab is in front of you — or as soon as you scroll a quarter of it. The campaign codes from the first link you arrived on are kept in this browser’s local storage, and a later visit that arrives with no codes of its own is counted against them, outreach code included; a link carrying its own codes is used for that visit, and the stored first touch is never replaced. If a link carries a different outreach code, that later touch is recorded alongside the first rather than in place of it. The referrer of that first visit is stored in the same place and is never sent anywhere — every visit sends the referrer it actually had. A return visit is still counted, and an outreach code is removed from the address bar after it is read. The page also asks this domain for Vercel’s analytics script; on 26 September 2026 we requested that address on each of the five hosts we publish — lilulab.ai, willcall, ninemin, ghmirror and pinpoint — and every one returned HTTP 404 and no script, so no script from another company was served to your browser and none ran. The page still asks for it on every load, so this stops being true the moment that address starts answering, without a single byte of this page changing — and this page will not know it has, and will go on saying what it says. Whether Vercel counts the request at its own edge is its record and not ours; as of 26 September 2026 no one here had opened it, and we keep no copy of it. No cookie; the local storage above does that job. Some things are recorded that the list above does not name. Your browser and the network attach these to the request rather than the page sending them: the identification string your browser gives with every request; the two-letter country the network assigns your address — no city, and your address itself is never stored; and the site address you asked for. Our own server then writes its own bookkeeping about the record: the date and time of your visit, to the thousandth of a second, from our clock; which request header it took that site address from; and a number naming the record format it wrote. It also stores the domain your browser said the count was fired from, which for a normal load of this page is this site itself, and which our server records as the word “unknown” when the browser sends nothing it can read. Every row is kept in Vercel’s blob storage and not on a machine of our own, measured 26 September 2026 by reading the handler. The page sends how long it was open twice, from two different counters, and our server reads only one of the two names — so some rows carry it and some are empty (measured 26 September 2026). This page reads an outreach code from the address if one is present and keeps it in your browser, which would let us tell one reader from another — but we have sent no link for this page and hold no list of recipients for it, so as of 2 October 2026 there is no name for any code to resolve to. This page also loads a second counter of its own. It sends its own copy of the view event on every load, so one load writes two view rows and any rate measured against them reads half its true value; it also measures the time differently, counting only the seconds the page was actually in front of you where the first counter sends wall-clock time since the page opened. It also sends an event once you have had the page in front of you for ten seconds and moved the mouse, touched the screen, scrolled or pressed a key. That ten-second event also depends on the count before it: this second counter does not send it unless its own copy of the view event went out first. Its “engaged” is stricter still — it is sent only after that ten-second event has gone out, you have had the page in front of you for ninety seconds in all, and you have scrolled far enough to reach a marker we put where the explaining part of this page ends; a reader who stops short of that marker never produces one. Because both counters send an event called “engaged” under different rules, a single visit can produce two of them. The host that serves it keeps its own request logs; those are its record and not ours. Every quotation above is from the linked file at the pinned commit; the same links are listed for machine readers at /llms.txt.