Why updating customer metadata fails with a 422 value_already_exist error
Last updated: June 30, 2026
When you update a customer through the Lago API and include a metadata item that already exists on that customer, the call can fail with a 422 value_already_exist error. The cause is that the metadata field is a full replacement rather than a key-based upsert: Lago matches each item by its internal ID, so an item sent without that ID is treated as a new entry, and the new entry collides with the existing key.
How customer metadata updates work
Customer metadata is identified by an internal ID, not by its key. When you send a metadata array on a customer update, Lago matches each item to an existing one by that ID and treats any item without an ID as a new entry to create. So if you send a metadata item by key and value alone, Lago does not read it as an edit of the existing item. It tries to add a new one with the same key, the per-customer key uniqueness check fails, and the call returns the 422 value_already_exist error.
The array you send is treated as the complete desired state for the customer, which produces two related behaviors:
Sending
metadata: []clears all metadata on the customer.Omitting an existing item from the payload deletes that item.
How to confirm the cause
Check the metadata array in the failing update request. If an item whose key already exists on the customer is being sent without that item's ID, this is the cause. Fetch the customer with GET /customers/{external_id} and compare: any metadata key in your payload that also appears on the customer, but without its ID attached, will be treated as a create and will trigger the 422.
How to resolve the issue
Read the existing metadata first and send each item's ID back on the update. The detail to watch is the field name: the customer GET response returns each item's ID as lago_id, but the update payload accepts it as id. Read lago_id from the response and send it back as id. Sending it under the name lago_id in the update payload does not work, because the update only recognizes id. The item is then treated as having no ID, Lago tries to create it, and you get the same 422.
The steps:
Fetch the customer with
GET /customers/{external_id}and read thelago_idof each metadata item you want to keep or update.Build your update with one entry per item you want on the customer, setting
idto the value you read fromlago_id, along withkey,value, anddisplay_in_invoice.For a genuinely new item not yet on the customer, leave
idout so Lago creates it.
Because the array is the full desired state, include every item you want the customer to keep, not only the one you are changing. Anything you omit is deleted.
If reading the IDs back is inconvenient, you can instead replace all metadata in two calls: send a first update with metadata: [] to clear what is there, then a second update with the full array you want, with no id on any item since none exist anymore. This avoids the fetch but makes two calls and leaves a brief window where the customer has no metadata. Prefer the ID approach when consistency matters or when other items must be preserved.
Field reference
The update payload identifies each metadata item by id, which is the value returned as lago_id on the customer GET response.
Field | Where it comes from | Notes |
|---|---|---|
| The | Required to update an existing item. Omit to create a new one. |
| Your data | Unique per customer. A duplicate key on a create is what triggers |
| Your data | The metadata value. |
| Your data | Whether the item shows on the invoice. |
This article reflects guidance drawn from customer case resolutions. It is not officially supported documentation and may not apply to all situations.