Almost every "my app works with test data but breaks in production" bug traces back to one thing: delegation. Understanding it is the single highest-leverage skill a Canvas App developer can have, because the failure is silent, you get wrong data, never an error message.
What delegation actually is
When you write Filter(Orders, Status = "Open"), Power Apps has two ways to run it:
- Delegated, it sends the filter to the data source (SharePoint, Dataverse, SQL) and gets back only the matching rows. Fast, and correct no matter how many rows exist.
- Non-delegated, it downloads the first 500 rows (raisable to 2000) and applies the filter locally in the app. If row 501 matched, you never see it.
What delegates, by data source
Delegation is per source. Dataverse and SQL are far more capable than SharePoint, most notably, they delegate aggregates that SharePoint cannot. This table covers the operations people hit most:
| Operation | SharePoint | Dataverse | SQL |
|---|---|---|---|
| =, <>, <, >, <=, >= | |||
| StartsWith() | |||
| Search() / EndsWith() | |||
| in (membership) | partial | partial | |
| And / Or / Not | |||
| Sum / Average / Min / Max | |||
| CountRows / CountIf | |||
| SortByColumns / Sort | |||
| Filter / LookUp / Search | * | ||
| AddColumns / GroupBy / Distinct |
* The function delegates, but only if what you pass it is also delegable, e.g. Filter(list, Search(...)) is non-delegable on SharePoint because Search() isn't. Table-shaping functions like AddColumns, GroupBy and Distinct run in-memory on every source.
Delegable search: StartsWith, not Search
The most common non-delegable trap is a "contains" search. On SharePoint, Search(), EndsWith() and the in operator all fall back to the row limit. StartsWith() delegates, so a prefix search stays correct at any scale:
Filter(Items, StartsWith(Title, txtSearch.Text))// "Contains" isn't a Power Fx function - these are the culprits:Filter(Items, Title in txtSearch.Text) // "in" - not delegable on SPFilter(Items, Search(Items, txtSearch.Text, "Title")) // Search() - not delegable on SPIf you genuinely need a mid-string match on SharePoint, your only correct options are to cache the list first (below) or move the column to a data source that delegates it.
Aggregates: fine on Dataverse/SQL, a trap on SharePoint
Sum, Average, Min, Max, CountRows and CountIf delegate on Dataverse and SQL but not on SharePoint. On a SharePoint list of 5,000 rows, Sum(Orders, Amount) quietly sums only the first 500 (or 2000):
Sum(Orders, Amount) // delegates on Dataverse / SQL, not SharePointCountRows(Filter(Orders, Status = "Open")) // same storyOn SharePoint, the reliable pattern is to cache the (delegably-filtered) rows locally, then aggregate the collection. Collections have no delegation limit because they already live in the app. This same client-computed-aggregate pattern is what makes unique keys racy, see the concurrency retry for the real-world failure and fix:
ClearCollect(colOpen, Filter(Orders, Status = "Open")); // = delegatesSum(colOpen, Amount) // local, exactCache-and-work-local
The escape hatch for almost any delegation problem is to pull a delegably-filtered slice into a collection with ClearCollect, then do the non-delegable work (Search, GroupBy, Sum) on that collection. The key is that the filter feeding ClearCollect must itself be delegable, or you're just caching the first 500 rows.
// Delegable pull (equality filter), then in-memory Search + GroupByClearCollect(colActive, Filter(Projects, Status = "Active"));// These are non-delegable, but colActive is already in memory - so it's fine:Filter(colActive, Search(colActive, txtFind.Text, "Name"));GroupBy(colActive, "Owner", "ByOwner")Raising the 500-row limit (and why it's not a fix)
Settings › General › Data row limit for non-delegable queries lets you raise the cap from 500 to a maximum of 2000. It buys headroom for small lists, but it is a band-aid: at 2,001 rows you're wrong again, and pulling 2000 rows into the client hurts load time and memory. Treat it as breathing room while you make the query delegable, not as a solution.
Quick checklist
- See a blue underline? Assume the result is capped and wrong on large data. Fix it, don't ignore it.
- Prefer
=andStartsWith()overSearch(),in, andEndsWith()on SharePoint. - Need Sum/Count on SharePoint? Cache a delegable slice first, then aggregate the collection.
- Building complex shaping (GroupBy, AddColumns)? Do it on a collection, never on the raw source.
- On Dataverse or SQL? You have far more room, but table-shaping functions still run in-memory.