LowCodeStacks™
Sign in

Power Apps ForAll Patch updates only the first record? Fix it

To patch multiple records, give Patch a table or loop with ForAll. If only the first record changes, the LookUp inside the loop compares a column with itself.

Power AppsDesign patterns10 min read

Checked against Microsoft Learn · 7 sourcesPosted by the Maker DeskHow we write guides

You tick several items in a gallery, press Done, and a formula loops over them with ForAll and Patch. Then only the first record in the data source changes. The others keep their old values. Power Apps can patch multiple records in one go. Microsoft's page on bulk records names this exact symptom, and the Patch reference explains the cause: inside the loop, the LookUp that should find each record compares a column with itself. This guide explains why that happens, the two fixes Microsoft documents, and when to drop the loop and give Patch a whole table instead.

The examples use a data source called Tasks with a number column Id and text columns Title and Status, and a collection called colChecked that holds the ticked tasks. Use your own names. The Patch reference says record shapes for column types such as Choice and Lookup vary by data source, and SharePoint's differ from Dataverse's. For SharePoint Person, Choice and Lookup columns, see how to patch Person, Choice and Lookup columns.

  • A button runs ForAll with Patch over the ticked items, but only the first record in the data source changes.
  • The same record is saved once for every item in the loop, and the records you meant to change aren't touched.
  • No error appears, because nothing in the formula is invalid.

Why only the first record changes

This formula looks right, and it's the one that fails:

powerfx
ForAll(
    colChecked,
    Patch(
        Tasks,
        LookUp(Tasks, Id = Id),
        { Status: "Done" }
    )
)

ForAll and LookUp each create a record scope. Inside the LookUp, the name Id means the Id of the Tasks record being checked. Microsoft's operators page explains the rule: field names added by a record scope override the same names from elsewhere. So Id = Id compares each task's Id with itself. That's true for every record, and LookUp returns the first one. Each pass of the loop patches that same first task.

Writing ThisRecord.Id on the right doesn't help. Learn says ThisRecord always refers to the innermost scope, which here is the LookUp, not the ForAll.

Why one task changes again and again
  1. Step 1ForAll takes one ticked row at a time
  2. Step 2LookUp(Tasks, Id = Id) reads both Ids from the same Tasks row
  3. Step 3Every task matches itself, so LookUp returns the first task
  4. Step 4Patch saves that first task on every pass
  5. Step 5Name the tables with As, and each LookUp finds its own task

Fix the LookUp: name each table with As

Microsoft's pages give two answers, and they don't agree:

  • The bulk records page (Create or update bulk records, dated June 2021) uses the disambiguation operator, colChecked[@Id], on the right-hand side. It says that without it, only the first record gets updated.
  • The Patch function reference (dated May 2026) shows a ForAll and LookUp written with A[@OrderId] on the right, and says Power Apps currently reads both sides as fields of the LookUp's scope. So the LookUp still finds the first row every time. Its fix is to name the LookUp's table with As, which it recommends, or to put ThisRecord on the left-hand side.

Our advice: use As. It follows the newer page and gives the same result the older page is aiming for. This is the Patch reference's pattern with our table names:

powerfx
ForAll(
    colChecked,
    Patch(
        Tasks,
        LookUp(Tasks As T, T.Id = colChecked[@Id]),
        { Status: "Done" }
    )
)

You can also name the loop's record. The ForAll reference says As can name the record being processed, and the operators page says giving each scope its own name makes every scope available in nested formulas. With both tables named, there's nothing left to guess:

powerfx
ForAll(
    colChecked As Checked,
    Patch(
        Tasks,
        LookUp(Tasks As T, T.Id = Checked.Id),
        { Status: "Done" }
    )
)

These follow Microsoft's documented syntax; we haven't run them in a tenant. Test with two or three records and check that each one changes.

When the records already come from the data source, skip LookUp

If you loop over records that came from the data source itself, each record can be the base record for Patch, so there's nothing to look up. Microsoft's operators page shows this shape, with With naming a filtered table first:

powerfx
With(
    { Overdue: Filter(Tasks, Status = "Overdue") },
    ForAll(
        Overdue As Task,
        Patch(Tasks, Task, { Status: "Escalated" })
    )
)

The loop runs over the filtered table, not over Tasks itself. The ForAll reference says you can't change the table that ForAll is looping over.

Learn says the disambiguation operator can't be used on a gallery's items. Its alternative adds a label to the gallery template, bound to the Id column, and compares with that label. Here the label is lblId, the checkbox is chkDone and the gallery is galTasks:

powerfx
ForAll(
    Filter(galTasks.AllItems, chkDone.Value = true),
    Patch(
        Tasks,
        LookUp(Tasks, Id = lblId.Text),
        { Status: "Done" }
    )
)

Learn's sample compares the Id with the label's text, as above. If your Id column is a number, such as SharePoint's ID, wrap the label in Value() so both sides are numbers: LookUp(Tasks, Id = Value(lblId.Text)).

Learn mentions a third way without a formula: when you make the local copy of your data, use AddColumns to add a renamed copy of the Id column (Learn calls it NewId). The loop then compares NewId with Id, and the two names can't collide.

Patch a table of records in one call

Patch doesn't need ForAll to change several records. The Patch reference documents a form that takes a table of base records and one or more tables of change records:

  • The records in each change table correspond one for one with the base records.
  • Each change table must have the same number of records as the base table, or an error occurs.
  • When a base record came from the data source, Patch finds and modifies it. For a single record, Learn says you must be able to trace it back to the data source, for example through a gallery's Items or a context variable. It doesn't say whether a collection keeps that link, so test with two or three records.
  • The result is a table, with each record corresponding one for one with the base and change records.

To build the change table, use ForAll inside Patch. The ForAll reference says it returns its results in the same order as the input table, so each change lines up with its record:

powerfx
IfError(
    Patch(
        Tasks,
        colChecked,
        ForAll(colChecked, { Status: "Done" })
    ),
    Notify("Some tasks weren't updated: " & FirstError.Message, NotificationType.Error)
)

Microsoft's code optimization page calls this order the better approach: Patch on the outside, with ForAll building the records inside it, instead of ForAll around Patch. Its own sample passes the ForAll table to Patch as the second argument, Patch(DataSource, ForAll(...)); the version above uses the reference's documented three-argument form. It also leaves no LookUp to go wrong. The IfError wrapper follows the Patch reference's own example for patching several records.

The bulk records page also shows a shorter form, Patch( DataSource, Collection ), for when the collection matches the data source. Its example is two collections with the same column names. The reference's syntax section only lists the form with change tables, so if the short form misbehaves in your app, use the one above.

Create many records at once

To create records, the bulk records page loops with ForAll and patches Defaults, which gives a new base record each time:

powerfx
ForAll(
    colNewTasks,
    Patch(
        Tasks,
        Defaults(Tasks),
        { Title: Title, Status: Status }
    )
)

The Patch reference does the same in one call, with a table of Defaults records and a table of changes of the same length:

powerfx
Patch(
    Tasks,
    Table(Defaults(Tasks), Defaults(Tasks)),
    Table(
        { Title: "Order the labels", Status: "Pending" },
        { Title: "Book the room", Status: "Pending" }
    )
)

Which pattern to use

Your situationUse
The records came from the data source and all get the same changeOne Patch with a table, with ForAll building the change records
The records to change match a conditionWith and Filter, then ForAll patching each record
Your collection only holds a copy of each IdForAll with LookUp, both tables named with As
The items are ticked in a gallery, with no collectionForAll over the gallery's AllItems, comparing with a label
New recordsForAll with Defaults, or one Patch with a table of Defaults

Delegation and performance

These notes are only what Microsoft's pages state:

  • Patch itself isn't delegated, and doesn't need to be. The Patch reference says it writes rather than queries. A delegation warning on a formula with Patch comes from the part that picks the records: Filter, LookUp or ForAll.
  • ForAll can't be delegated. When it runs over a data source, only the first part of the data is retrieved before the formula runs. Learn's page on data sources puts that first part at 500 records by default, and up to 10,000 if you raise the limit in the app's settings. So ForAll(Tasks, ...) straight over a large table only reaches its first records. Narrow the records with a delegable Filter first, and keep the table you loop over small.
  • A LookUp inside a loop is one query per record. Microsoft's performance guidance calls this the Select N+1 pattern: one query for the list, then one more for each item. It's another reason to prefer one Patch with a table.
  • Don't depend on the order. The ForAll reference says records can be processed in any order and, when possible, in parallel. Microsoft's code optimization page says the opposite: that ForAll looks at one record at a time. Either way, don't write a loop whose result depends on the order, such as numbering records as you go. UpdateContext, Clear and ClearCollect can't be used inside ForAll.

For a change that should reach every matching record in a big list, our delegation cheat sheet covers UpdateIf, RemoveIf and when a flow is the better tool.

Do

  • Name both tables with As whenever a LookUp sits inside a ForAll.
  • Prefer one Patch with a table when the records came from the data source.
  • Wrap the bulk Patch in IfError and tell the user which save failed.
  • Test with two or three records and check that each one changed.

Don't

  • Don't write LookUp(Tasks, Id = Id) or Id = ThisRecord.Id inside a loop: both compare the column with itself.
  • Don't change the table that ForAll is looping over.
  • Don't run ForAll straight over a large data source and expect it to reach every record.

If the bulk save fails with a banner rather than changing the wrong record, see The requested operation is invalid. For a slow app overall, start with canvas app performance.

Sources

Add LowCodeStacks as a preferred source on Google

Was this helpful?

Comments

No comments yet. Share what worked for you.

Sign in to comment. Comments show under a display name you choose, never your email.