Patching Records in Canvas Apps: Creates, Updates, and the Errors You'll Get
The Patch function is how you create and update records in canvas apps. Here's the syntax for every column type, including lookups and choices, plus fixes for the errors you'll definitely hit.
The Patch function is one of the first things you need to learn in canvas apps. It creates records, updates records, and is responsible for more forum posts asking “why isn’t this working” than probably any other function.
Here’s how it works, with real examples for every column type you’ll run into.
Creating a New Record
The basic syntax for creating a new record in Dataverse:
Patch(
Accounts,
Defaults(Accounts),
{
'Account Name': "Contoso Ltd",
'Main Phone': "555-0100"
}
)
Defaults(Accounts) tells Patch you’re creating a new record. Without it, Patch doesn’t know whether to create or update.
Updating an Existing Record
To update, pass the existing record instead of Defaults():
Patch(
Accounts,
LookUp(Accounts, 'Account Name' = "Contoso Ltd"),
{
'Main Phone': "555-0200"
}
)
Or if you already have the record in a variable or gallery context:
Patch(
Accounts,
ThisItem,
{
'Main Phone': "555-0200"
}
)
Column Types That Trip Everyone Up
Text, Number, Date — The Easy Ones
These work exactly how you’d expect:
Patch(
Contacts,
Defaults(Contacts),
{
'First Name': "John",
'Last Name': "Smith",
'Annual Revenue': 50000,
'Birth Date': DateValue("1990-03-15")
}
)
Choice Columns
Choice columns need a specific syntax. You can’t just pass a string.
// Wrong - this will error
Patch(Contacts, Defaults(Contacts), { 'Preferred Method of Contact': "Email" })
// Right - reference the choice value from the enum
Patch(
Contacts,
Defaults(Contacts),
{
'Preferred Method of Contact': 'Preferred Method of Contact'.Email
}
)
The pattern is 'Column Name'.'Option Label'. Power Apps gives you IntelliSense for this — start typing the column name and it will show you the available options.
Lookup Columns
This is where most people get stuck. Lookups need a record reference, not just a GUID or a name.
// Wrong - passing a plain GUID
Patch(Contacts, Defaults(Contacts), { 'Company Name': varAccountId })
// Right - pass the full record
Patch(
Contacts,
Defaults(Contacts),
{
'Company Name': LookUp(Accounts, Account = varAccountId)
}
)
If you already have the record (from a gallery selection, a variable, etc.), just pass it directly:
Patch(
Contacts,
Defaults(Contacts),
{
'Company Name': Gallery1.Selected
}
)
Clearing a Lookup Value
To set a lookup to blank:
Patch(
Contacts,
ThisItem,
{
'Company Name': Blank()
}
)
Yes/No (Boolean) Columns
These are straightforward — just use true or false:
Patch(
Contacts,
Defaults(Contacts),
{
'Do Not Email': true
}
)
Patching Multiple Records at Once
You can pass a table of records to Patch. This is much faster than calling Patch inside a ForAll loop because it batches the operations:
Patch(
Contacts,
ForAll(
colNewContacts,
{
'First Name': ThisRecord.FirstName,
'Last Name': ThisRecord.LastName,
'Company Name': LookUp(Accounts, 'Account Name' = ThisRecord.Company)
}
)
)
Important: When creating multiple records this way, don’t include Defaults(). Patch handles it automatically when you pass a table without primary key values.
Saving the Result
Patch returns the created or updated record. Capture it in a variable if you need the new record’s ID:
Set(
varNewAccount,
Patch(
Accounts,
Defaults(Accounts),
{ 'Account Name': "Contoso Ltd" }
)
);
// Now varNewAccount.Account contains the GUID of the new record
This is particularly useful when you need to create a parent record first, then create related child records that reference it.
Checking for Errors
Patch doesn’t throw a visible error by default if something goes wrong. You need to check explicitly:
Set(
varNewRecord,
Patch(
Accounts,
Defaults(Accounts),
{ 'Account Name': "Contoso Ltd" }
)
);
If(
IsError(varNewRecord),
Notify("Failed to save. Check your input.", NotificationType.Error),
Notify("Saved successfully.", NotificationType.Success)
)
You can also use IfError for inline error handling:
IfError(
Patch(Accounts, Defaults(Accounts), { 'Account Name': "Contoso Ltd" }),
Notify("Save failed: " & FirstError.Message, NotificationType.Error)
)
Patch vs SubmitForm
If you’re using an Edit Form control, you don’t need Patch at all. SubmitForm(Form1) handles creating and updating based on the form’s mode. Use Patch when:
- You’re not using a form control (building a custom UI with individual inputs)
- You need to create related records in sequence
- You’re doing bulk operations
- You need to set values that aren’t on the form
Common Mistakes and How to Fix Them
”The value passed to ‘Patch’ for the column cannot be used”
This usually means you’re passing the wrong data type. The most common cause: trying to pass a text string to a lookup column or a choice column. Check the column type and use the correct syntax above.
”Network error when using Patch function”
This often isn’t a real network error. It usually means a required field is missing or a business rule on the Dataverse side rejected the data. Check the Dataverse table’s required columns and any business rules or plugins that fire on create/update.
Patch Succeeds But the Value Is Empty
If you’re patching a lookup and the target record appears to save but the lookup column is blank, you’re probably passing the wrong type. A common mistake:
// This looks right but sets the lookup to blank
Patch(Contacts, ThisItem, { 'Company Name': Gallery1.Selected.'Account Name' })
That passes the text name of the account, not the record. You need:
Patch(Contacts, ThisItem, { 'Company Name': Gallery1.Selected })
ForAll + Patch Is Slow
If you’re looping through records with ForAll and calling Patch for each one, you’re making one API call per record. Instead, pass the whole table to Patch in one call (see the “Patching Multiple Records” section above). The difference between 200 individual calls and 1 batch call is significant.
Patch Creates a Duplicate Instead of Updating
This happens when the record reference in the second argument doesn’t include the primary key. Make sure you’re passing an actual record from the data source, not a manually constructed record without the ID field.
// This creates a new record every time (no ID)
Patch(Accounts, { 'Account Name': "Contoso" }, { 'Main Phone': "555-0100" })
// This updates the existing record (has the real record reference)
Patch(Accounts, LookUp(Accounts, 'Account Name' = "Contoso"), { 'Main Phone': "555-0100" })
Related articles
Cascading Dropdowns in Canvas Apps (Yes, It's Harder Than It Should Be)
A parent dropdown for Country, a child dropdown for City that only shows cities in the selected country. Here's the pattern, the delegation-safe version, and the reset behavior that catches everyone.
Lookup Columns in Power Apps: Create, Display, and Patch Without the Guesswork
Lookups are how Dataverse tables relate to each other, and they're the column type that causes the most confusion in Power Apps. Here's how to set them up and work with them in canvas apps.
Get the Current User in Power Apps
The User() function gets you started, but most real scenarios need more. Here's how to get the current user's details from Dataverse, including Business Unit, Team membership, and security role.