All articles

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.

· 9 min read

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" })
Share this article LinkedIn X / Twitter

Related articles