n8n logon8n Automation Hub
Debugging notes

Three Google Sheets Node Behaviors That Cost Me Two Rows of Real Production Data

Build breakdown · n8n workflow · August 26, 2026

Adding new rows to a publish-queue spreadsheet is the kind of task that feels too simple to go wrong. It went wrong twice in the same session, and the second time it silently overwrote a row that had a real, already-published video's data sitting in it.

Workflow at a glance
  1. Posting a JSON array to an n8n Webhook node does not auto-split it into multiple items downstream
  2. A Sheets node fed straight from that webhook wrote one garbage row instead of the intended ten
  3. Switching the Sheets operation to appendOrUpdate, matched on a column that repeats across records, overwrote an unrelated live row
  4. The overwritten row belonged to an already-published item, wiping its real published status back to blank
  5. Recovered from a pre-edit read; permanent fix uses plain append with no matching key at all
Screenshot of the actual n8n workflow canvas: the publisher whose queue sheet this append-and-matching-key incident happened against

The actual workflow canvas, straight from my n8n instance.

The setup: a temporary workflow to append a batch of rows

The pattern is normally simple — a throwaway webhook-triggered workflow that takes a JSON array in the request body and appends each item as a new row to a queue sheet. Ten new items needed to go in, so the array had ten objects in it, POSTed once.

Bug one: a webhook does not split an array into items

A Webhook node receiving a JSON array as its POST body does not fan that array out into multiple items the way it might look like it should. It produces exactly one item downstream, with the entire array sitting nested inside that single item's body field. A Sheets node connected directly to that webhook, set to map fields automatically, mapped the item's own top-level properties instead — headers, params, the raw request metadata — and wrote one row of webhook envelope garbage instead of ten real rows. No error, no warning; the node did exactly what it was told, the request just wasn't shaped the way the rest of the workflow assumed. The fix is a small Code node between the webhook and the Sheets node that reads the array out of the body and returns one output item per entry — the standard way to turn a single posted array into N items for anything downstream that expects to process them individually.

Bug two: a matching key that isn't actually unique

With the array-splitting fixed, the Sheets node was set to update-or-insert, matched on an ID column. That ID looks unique per row at a glance, but it isn't — the same short identifier is intentionally reused across multiple unrelated batches in this sheet, because it labels a *position* within a batch, not a specific record. The update-or-insert operation matched a new row against an existing row from a completely different, already-published batch that happened to share the same ID, and overwrote it — title, path, and status all replaced, including the field holding that older row's real, live publish confirmation. It went from a fully published, done row back to an empty, not-yet-published one, silently, with the operation reporting success.

What actually stops this

The permanent fix was smaller than the debugging: switch the operation from update-or-insert to a plain insert with no matching column at all. This sheet's only genuinely unique value is its literal row number, assigned at insert time — nothing else in it is safe to match on, however unique it looks. Update-or-insert is reserved for the narrow case of editing a row you already know the row number of; inserting new rows never uses it here again.

Recovering the overwritten row

The row had been read moments before the bad write, as part of the same debugging session — that earlier read was the only reason the original values could be restored at all. Rebuilding it meant manually re-entering the title, path, status, and the real publish ID from that pre-edit snapshot, then a follow-up read to confirm the restored row matched what had been live before. Without that earlier read, the original publish confirmation would have been gone for good — the sheet has no version history for a single-cell edit like this.

The rule that came out of it

A node reporting success is proof it did *something*, not proof it did the intended thing. Any Sheets write from now on gets a read immediately before and immediately after, and any matching key gets confirmed as actually unique across the whole sheet before it's trusted — not assumed unique because the format looks like an ID.

Worried a routine Sheets update could quietly overwrite live data?

I review and harden n8n's Google Sheets integrations — matching keys, array handling, and the read-before/read-after habits that catch a bad write before it costs you real data.