TL;DR
Form state carries at most twenty-five rows, and the warning is an instruction a builder can delete. The cap is on the form path, not the data path.
Yes — form state carries at most twenty-five rows. The agent does not error when it hits the ceiling. It answers, and the answer is built on whatever arrived.
The part worth knowing is where the cap applies and where it does not, because that distinction is also the fix.
Is there a documented row cap?
There is, and it sits in the agent instruction block Microsoft publishes as a starting point:
"A tool call response can include up to 25 rows of data as form state. Generate a warning if the form state contains 25 rows of data."
Two separate things live in that sentence, and separating them is the whole article:
| The sentence says | What it is |
|---|---|
| A response carries at most twenty-five rows of form state | A property of the platform. Not configurable, not negotiable |
| Generate a warning when it does | An instruction to the model. Text in a prompt |
The cap is real whatever you do. The warning exists only if the instruction survives into your agent.
Does hitting the cap produce an error?
No, and that is the design worth understanding. Microsoft's mitigation is a behavioural one — it asks the model to notice and say so, rather than the platform refusing.
Which means the failure is silent by default, and the boundary condition is exactly the one that reads as success: a full page of results. An agent that receives twenty-five rows has no way to know whether the answer was twenty-five or two hundred. It sees a complete-looking set and reasons over it.
Microsoft's instruction block guards a closely related failure in the same section, and the pairing is telling:
"When instructed to create new data, and the creation fails, DO NOT retrieve existing data instead."
Both rules exist to stop an agent reporting a partial or substituted result as a whole one — which is the false-success problem in its read-side form.
Do not delete either line when you customise the instructions. They read like boilerplate and they are the two sentences doing correctness work.
Why is twenty-five rows easier to hit than it sounds?
Because the filtering that would narrow a grid below the ceiling is itself limited. Limitation eight of ten:
"The MCP server doesn't support advanced filters on grids. For example, it doesn't support the 'before,' 'after,' and 'between' operators for date columns. It supports only the 'matches' operator for filtering."
No date range. For a transactional ERP that is the single most natural way to cut a result set down, and on the form path it is unavailable. Ask about open orders in a period and the grid you are filtering is the whole grid.
There is one published exception worth knowing, because it reads like an escape hatch and is narrower than it looks: (lessThanDate(x:int)) "is a valid value for a grid date column filter" — a relative, one-sided comparison, not a range.
Two other form behaviours compound it. Tabs "are closed by default" and the agent must open them to reach controls, and the form_find_controls tool "accepts only one search term per call." More calls to assemble less context — and outside Copilot Studio every one of those calls is billed.
What does a truncated answer look like from the outside?
Like a good one, which is the entire problem.
A reviewer reading the agent's reply sees a well-formed list of records, correctly retrieved, correctly described. There is no ellipsis, no "showing 25 of 212", no marker of any kind — the response is simply built from a smaller set than the question implied.
Compare that with the failure modes people design for. A timeout is visible. A permission error names itself. A wrong value can be spotted by someone who knows the data. A short answer that is internally consistent survives every one of those checks, because nothing about it is wrong except its completeness.
That is why the mitigation Microsoft chose is a warning rather than a validation: there is nothing for the platform to reject. The response is valid. It is the inference drawn from it that is not.
Does the cap apply to data tools as well?
Read Microsoft's words closely: the limit is on rows "as form state."
Form state is what the form path returns. The data tools are a different family — they read through data entities over OData, and Microsoft describes that path as needing "fewer tool calls" and giving "better performance" for standard operations.
So the honest answer is that the twenty-five-row sentence is scoped to form state and does not, on its face, describe the OData read path. We are not asserting that data tools are uncapped — Microsoft does not say that either, and an unbounded claim about a limit that is not documented would be a guess. What is documented is the scope of this cap, and it is the form path.
That makes tool-family choice a correctness decision and not only a performance one, which is the routing question this cluster's comparison covers.
What should you actually do about it?
Three moves, in order of return. These are ours:
- Keep the warning instruction, verbatim. It costs one line and it is the only thing that surfaces the condition at all. Treat it as load-bearing when you rewrite the prompt.
- Route list-shaped reads to data tools. If the job is "find the records", that is the family Microsoft tells you to prefer anyway, and the documented cap is on the other path.
- Design the question to be narrow. With only a matches operator on the form path, narrowing has to come from the request rather than the filter — one customer, one order, one item, rather than a period.
And treat a twenty-five-row answer as unproven. Not wrong, unproven. That is a cheap habit and it is the one that catches the case where the number mattered.
About Cognilium Cognilium builds AI optimization apps for Microsoft Dynamics 365 — companion apps that optimize the pricing, inventory, warehouse and planning decisions your ERP manages but can't optimize. Dynamics is your system of record. Cognilium is your system of intelligence. https://cognilium.ai · https://www.linkedin.com/company/37180269/
Agentic ERP. We build turnkey AI optimization apps for Dynamics 365 — sidecar applications that run in your own Azure tenancy and solve the calculations a standard ERP is not built to compute. Built on Azure OpenAI, Microsoft Fabric and Copilot Studio. We build these on request, against your data and your environment.
More on the boundary between what Copilot does and what you build: Agentic ERP on Dynamics 365.
If an agent is giving you answers that look complete, the row cap is worth ten minutes of someone's attention. Bring a query that returned a full page to a 15-minute call.
Sources
Sources and fact-check
| # | § | Claim | Tier | Source | Verdict |
|---|---|---|---|---|---|
| 1 | 1 | The 25-row sentence, quoted verbatim in full | T1 — verbatim | build-agent-mcp, # Extraction Instructions | PASS — load-bearing |
| 2 | 1 | The sentence contains a platform property and a model instruction | T1 — read off the quotation, which is two sentences | Same line | PASS |
| 3a | 3a | A truncated response carries no marker and survives ordinary review | T2 — ours, an argument about reviewability, stated as ours | — | PASS |
| 3 | 2 | No error is raised; the mitigation is behavioural | T2 — ours, and bounded: read from the fact that Microsoft's only published mitigation is an instruction to warn. Not stated as "Microsoft says it does not error" | Reading of claim 1 | PASS |
| 4 | 2 | "When instructed to create new data, and the creation fails, DO NOT retrieve existing data instead." | T1 — verbatim | Same instruction block | PASS |
| 5 | 3 | Advanced-filter limitation, quoted with the operator list intact | T1 — verbatim | copilot-mcp, limitation 8 | PASS — load-bearing |
| 6 | 3 | (lessThanDate(x:int)) is valid for a grid date column filter | T1 — verbatim | build-agent-mcp, # Form Tool Parameter Filling Instructions | PASS |
| 7 | 3 | Tabs closed by default | T1 — verbatim | copilot-mcp, limitation 5 | PASS |
| 8 | 3 | form_find_controls takes one search term per call | T1 — verbatim | copilot-mcp, form-tool table | PASS |
| 9 | 3 | Calls are billed outside Copilot Studio | T1 | copilot-mcp, Licensing — 0.1 Copilot Credits per tool call for other agent clients | PASS |
| 10 | 4 | The cap is scoped to "form state" | T1 — read off claim 1's wording | Same line | PASS — load-bearing |
| 11 | 4 | Data tools need "fewer tool calls" and give "better performance" | T1 — verbatim | copilot-mcp, Data tools | PASS |
| 12 | 4 | We do NOT claim data tools are uncapped — the body says so explicitly | T2 — ours, and it is a refusal to make an absence claim | — | PASS |
| 13 | 5 | The three moves | T2 — ours, labelled as ours in the body | — | PASS |
Tier summary: 10 × T1 (8 verbatim, 2 read off a quotation), 4 × T2 — 0 × T4.
The absence claim this article deliberately does NOT make. The obvious next sentence — "so data tools have no row cap" — is unsupported. Microsoft scopes the twenty-five to form state and publishes nothing about an OData ceiling, and an absence found by not seeing it is not evidence. §4 says this in the body rather than hiding it in a fact-check row, because a reader will reach for that inference themselves.
Share this article
Copilot Boundary
Topic Cluster
What Microsoft ships, what it does not, and the layer we build where a general assistant runs out.




