InAir's Latest Insights on Airtable

Write Collisions: What Happens When Two Systems Touch the Same Record at the Same Time

Written by Julia Eboli | Nov 6, 2026, 6:52:47 PM


A write that succeeds isn't the same as a correct write. An integration can send a valid request, get a clean response, and still leave the wrong value in the record. Nothing errors, because from the integration's point of view, nothing went wrong.

This post separates the three ways that happen. Only one of them needs two systems writing at the same moment. The other two can happen minutes apart, or with a single system doing all the writing. They look alike from the outside, but each has its own root cause and its own fix, so it's worth knowing which one you're dealing with.

 

Case 1: The Stale Overwrite

 

What happens

  • 9:00 An enrichment job reads a customer record from Airtable, including the Phone field.
  • 9:03 A support agent updates Phone in Airtable after a call.
  • 9:05 The job finishes and writes the record back: the Company Size it looked up, plus every other field it read at 9:00, including the old phone number.

The agent's update is gone. The job's request was valid, Airtable accepted it, and nothing flags that a newer value was replaced.

Root cause

The write carried a value that was correct when it was read and outdated by the time it landed. Nothing compared the two. The same thing happens when a retried request arrives after a newer update has already been applied.

How much gets overwritten depends partly on how the write is made. In Airtable's API, a PATCH request updates only the fields included in the request, while a PUT request performs a destructive update and clears every field it doesn't include. Either way, a job that sends back fields it never changed is overwriting values it has no reason to touch.

The fix

  1. Write only the fields the process owns or changed. The enrichment job owns Company Size, so its update should contain Company Size and nothing else. That alone removes many stale overwrites.
  2. Compare before writing the fields it does own. Add a field to each Airtable record that stores the timestamp or version of the last write applied to it. Reject any incoming write with an older timestamp or lower version. For a job that reads, processes, and then writes, the equivalent is to add a Last modified time field to the table, check it just before writing, and stop if it has changed since the read.
  3. Hold rejected writes instead of discarding them. A write that fails the check isn't always wrong. The enrichment job's Company Size may be exactly the update the record needs, rejected only because someone edited the record after the job read it. Send each rejected write to a dead letter queue table (a table that holds writes that failed a check) with the record ID, the field, the rejected value, the timestamp or version the job read, the reason, and a status. A person can then review it, or the job can re-read the record, run the step 2 check again, and write only if it passes.

Writes that arrive in the wrong order are a related but separate problem, covered in out-of-order updates.

How to test for it

In a sandbox, start the job, edit one of the fields it reads while it's still running, and confirm the edit survives once the job finishes.

 

Case 2: The Duplicate Nobody Asked For

 

What happens

  • A CRM sends a contact to Airtable with the company name "Acme Corp " (with a trailing space).
  • The integration searches Airtable for a record with that exact name and finds none.
  • The integration is set to create a record when no match exists, so it creates a second Acme Corp.

Every later update now lands on one of the two copies. Reports split across them, and nothing logged an error.

Root cause

The match ran on an unstable key. Names and emails get retyped, reformatted, and changed, and any difference produces a miss. The automatic insert then turned a harmless miss into a new record.

Airtable's API behaves the same way when used for upserts. An upsert matches incoming records on one to three fields named in fieldsToMergeOn. If no existing record matches, a new one is created. If one matches, it's updated. If more than one matches, the request fails. Merging on a name field carries the same duplicate risk as any name-based match. Merging on a stable ID field doesn't. The failure on multiple matches also has a use: if exact duplicates already exist, for example from a create that fired twice, upserts against them fail instead of guessing which copy to update.

The fix

  1. Match on an immutable ID stored in both systems, such as the CRM's contact ID, never on a name or email.
  2. Separate the update flow from the create flow. An upsert can't do this, because it always creates a record when nothing matches, so the integration needs a different flow: search Airtable for the CRM's ID first, then update the record only if a match is found. A record with no match isn't created automatically. Still, it goes to a review step, or to a separate, deliberate create flow that validates the record as legitimate and stores the CRM's ID on the new Airtable record, so later syncs can match on it.

The duplicate case is covered in full in why your integrations create duplicates.

How to test for it

In a sandbox CRM, duplicate a contact, so the copy has the same name and details but its own CRM ID. Send an update for the original, then for the copy. The original's update should change its matching Airtable record. The copy's update should change nothing in Airtable and land in the review step. If the copy's update changes the original's Airtable record, the integration is matching on something other than the CRM's ID, such as the name. If it creates a new record, the update and create flows aren't separate.

 

Case 3: The Concurrent Write Collision

 

What happens

  • 14:00:01 The billing system updates a customer's billing email in Airtable.
  • 14:00:02 A CRM sync pushes a different billing email for the same customer to the same field.
  • Both requests succeed. The field ends up holding whichever write arrived last.

If the two writes had arrived in the opposite order, the result would have been the opposite value. The outcome depends on arrival order, not on which value is correct.

Root cause

Two systems can write the same field, and nothing decides which one should win. Two systems have write access to the same field, and no rule says whose value should stand. Airtable applies each valid write as it arrives, so the field holds whichever came last. Neither system is malfunctioning: each sends a value that's correct from its own point of view, and each gets a success response. Without a rule deciding between them, arrival order decides instead, and neither system is told that its value was replaced.

The fix

Choose one of three approaches, in order of preference:

  1. Designate a single writer for the field. One system is authoritative for billing email, and it's the only place the value can change. The other system's integration is set up to read the field but never write it. If that system's users need to correct a billing email, they make the change in the authoritative system, or send it there as a request that a person or a defined rule approves before the value changes. With one place deciding the value, Airtable only ever receives one version of it. Setting that up is covered in Cross-System Source of Truth: who owns a field when Airtable isn't the only system writing to it.

  2. Route competing writes through a queue that applies them in a defined order rather than arrival order. It's more infrastructure to build and maintain, but it makes the outcome predictable.
  3. Hold conflicting writes for review when two arrive for the same field within a short window, and have a person decide.

Between Airtable bases, InAir's guide to Parent and Child Ownership Models applies the single-writer approach. The child base doesn't edit the master record directly. Its change is synced back to the parent base, where an automation writes the new value to the master record.

How to test for it

In a sandbox, send two conflicting writes to the same field a second apart, then repeat with the order reversed. If the final value changes with the order, the field has no rule deciding the outcome.

Telling Them Apart

The three cases often get lumped together as "sync problems," but the symptoms point to different causes.

These are the three ways a successful write still leaves an incorrect record, as set out in the comparison of connectivity and orchestration in InAir's guide to running Airtable inside complex system landscapes. Concurrent collisions trace back to one gap: no written rule for which system owns a field. Ownership is one of the two containment factors in InAir's Integration Load framework. Stale overwrites are partly an ownership problem, when a process sends back fields it doesn't own, and partly an ordering problem, when a write arrives late. Duplicates are a matching problem, which an ownership rule on its own doesn't fix.

Apart from the upsert failure once exact duplicates exist, none of the three shows up in an error log. The tests above are how you find out whether they're already happening.

If you want to know which of the three are already in your data and fix them without interrupting the syncs that are running, Schedule a Discovery Call with InAir, and we'll trace them with you.