Article · Jan 23, 2026

Silent failures: the bug class no AI tool catches in your data pipeline

AI tools ship pipelines that pass every test and then quietly drop data in production. Here are 4 silent failures I diagnosed in a Bubble + n8n migration, and the fixes.

If you have run an AI-assisted data migration into Bubble.io, n8n, Airtable, or any other no-code or low-code platform, you have probably hit the bug class that does not produce errors. The pipeline runs. Every node turns green. The notification says “completed successfully.” Then a week later, someone notices that the production data is missing rows, or the parent record has zero children when it should have eleven, or every record after the 137th in each batch is just gone. These are silent failures. They are the most expensive bug class in 2026 production data work, and they are the bug class no AI coding tool will catch for you, because they leave no signal for the model to read.

This post is a case study from a recent Bubble.io + n8n migration I ran for a client. The migration moved a complex relational schema from a source platform into a destination Bubble app. In phase 3 of the work, the pipeline hit four silent failures in production. Each one looked different, each one had a different fix, and all four shared the same root signature: the pipeline reported success, the destination data did not match the source, and the only way to find the gap was to re-query reality.

What is a silent failure, exactly?

A silent failure is a production pipeline failure that does not raise an exception, return a non-2xx HTTP response, or trigger any alert. The pipeline operator sees only the success path. The data layer sees the corruption.

The five most common causes in modern data pipelines:

  • Schema changes downstream of an unmonitored API. A column rename or a wrapped payload turns every read into undefined, but the API still returns 200 and the workflow still processes the (empty) result.
  • Data-quality drift. Missing values, format inconsistencies, duplicate rows. Each one looks like a single bad record on its own; in aggregate they degrade the dataset until something catches.
  • Platform-side timeouts that return success before work completes. An n8n Code node hits its 60-second cap and returns whatever it has finished. The downstream nodes proceed as if the input is complete.
  • Idempotency violations on retry. A pipeline restarts from a checkpoint and re-inserts records that already exist, doubling rows.
  • One-sided relationship updates. A child record points at its parent, but the parent’s list of children does not get updated. From the child’s perspective everything is fine; from the parent’s perspective the child does not exist.

The unifying property: in all five, the pipeline’s own success log is wrong, and the database’s actual state is the only ground truth. Pipelines that trust the success log fail silently. Pipelines that re-query reality do not.

The four silent failures, in the order they hit

The migration was a relational schema with three nested levels: top-level records (call them A), middle-level (B) attached to each A as children, and a leaf level (C) attached to each B. Source platform exposed all three via REST. Destination was a Bubble.io app with the standard Things-and-relationships data model. Orchestration was n8n Cloud (Pro tier).

1. The execution cap that swallowed half the batch

The first failure was the loudest. After a four-hour run, the verification step reported the destination had 1,037 records but the source had 1,676. 639 records missing, with no errors logged anywhere.

Diagnosis took the count-back pattern. I pulled the count at every stage:

  • Source: 1,676.
  • After the n8n Read node: 1,676.
  • After the n8n Transform node: 1,676.
  • After the n8n Code node that did the dedup-and-write logic: 1,037.

The Code node was the gap. It was processing the records in a single loop, and it had hit n8n Cloud’s 60-second task-runner timeout. When the timeout fires, the runner returns whatever it has produced so far and the workflow continues with the (truncated) result. There is no error.

The fix is the dispatcher and worker pattern. A parent workflow chunks the input into batches small enough to fit under the timeout, and triggers a child workflow per chunk via a webhook trigger. Every chunk starts a fresh execution clock. The parent waits for each child to complete before triggering the next. Total wall-clock time was longer, but every record landed. (Full breakdown in the n8n Cloud execution-cap post.)

2. The list field that pointed forward but not back

After the first fix, all 1,676 records landed. Verification on the child side passed. Verification on the parent side failed.

Symptom: each B record correctly named its parent A. But when the app rendered an A record’s “list of children,” the list was empty. Bubble’s repeating group showed nothing, even though the data was there.

This is a Bubble.io-specific silent failure rooted in how the platform models relationships. Pointing a child at a parent does not automatically add the child to the parent’s list-field of children. The child record has the parent reference. The parent record’s list field is unchanged. Bubble’s UI generally hides this, because the platform’s “Search for Bs whose Parent is A” expression goes around the list field and queries directly. But any code path that reads the list field (a list-of-things field, a custom state, an API export) sees an empty list.

The fix was a finalize-parents pass. After the children were written, a second n8n workflow ran over each parent A, queried for its actual children using a search, and wrote the resulting list back to the parent’s list-field. After that pass, parent-side reads matched child-side reads. (Full breakdown in the re-query database post.)

3. The fingerprint that included a mutable field

The third silent failure showed up on a re-run. After fixing the first two issues and starting a fresh pass on a different source, the destination ended up with duplicate B records. Some B records appeared twice. The same source row was being inserted as two different destination rows.

The dedup logic was using a fingerprint hash. For each incoming record, it would hash a subset of fields, check the destination for an existing row with that fingerprint, and update vs insert accordingly. The hash was deterministic. The bug was that one of the input fields to the hash was the source platform’s last_modified_at timestamp, which changed on every re-export.

Same logical record, different timestamp, different fingerprint, “no existing row matches,” insert as new. Duplicate.

Fixing this is what the natural-key fingerprint pattern addresses. The fingerprint inputs must be stable across re-runs and uniquely identify the logical record, never the physical version. Mutable fields, timestamps, auto-IDs, and platform-assigned UUIDs all need to be excluded. The fingerprint becomes the record’s name plus its serial number, or the email plus the tenant ID, or whatever subset is genuinely immutable. (Full breakdown in the natural-key fingerprint post.)

4. The webhook that returned 200 before the write committed

The fourth failure was the subtlest. Phase 3 added a step where the n8n pipeline called a Bubble API workflow via webhook to perform an authenticated write. The Bubble API workflow returned 200 immediately, the n8n pipeline marked the step complete, and continued to the next record.

Bubble’s API workflow execution is asynchronous by default. Returning 200 means the workflow was queued, not that the database write had committed. Under load, the queue lagged. The next n8n step would then look for the just-written record and not find it, because the previous write was still pending. The pipeline would log “record not found, skipping,” continue to the next, and the destination would end up with gaps.

Fix here had three parts: switch the Bubble API workflow to a synchronous call (response only after write commits), add a verify-existence retry loop on the n8n side that re-queries until the just-written record actually appears (with a sane upper bound), and treat the absence of a verdict on the verification step as a bug, not as expected drift.

The general rule

After the four fixes, the pipeline ran clean: 1,676 records and 722 parent links, all accounted for, zero silent failures across the verified run. The general rule that emerged:

Verify state, not operations. Re-query reality before treating any pipeline run as done.

Every silent failure I have found in five years of production data work shares the same anti-pattern: the operator trusted the platform’s success report. Every fix shares the same correction: re-query the destination database for absolute counts and relationships, compare to the source, and treat any non-zero delta as a bug to investigate. The platform’s success message is a hint, not a verdict.

If you are running AI-assisted data migrations into Bubble, n8n, Airtable, or any other low-code or no-code platform across North America, the UK and Ireland, the EU and EEA, or the ANZ region, and you suspect your pipeline is reporting success while quietly losing rows, let’s talk. The four-fix playbook above is what gets installed on day one of every migration engagement.

Frequently asked questions

What is a silent failure in a data pipeline?

A silent failure is a pipeline failure that does not produce an error, alert, or red flag. The pipeline runs, every node reports success, the dashboard shows green, and the production data is quietly missing, duplicated, or wrong. The five most common causes in 2026 are schema changes downstream of an unmonitored API, data-quality drift (missing values, format inconsistencies, duplicates), platform-side timeouts that return success before work completes, idempotency violations during retries, and child-to-parent relationship updates that point one direction but not the other. Each fails the same way: tests pass, alerts stay silent, the data is wrong.

Why don't AI coding tools catch silent failures?

Because AI tools optimize for the visible signal. The build succeeds, the tests pass, the workflow run shows green checkmarks across every node. There is no error trace for the model to read, no exception for the test runner to surface. Silent failures only become visible when you compare the pipeline's *report* of what it did against the database's *actual* state. AI tools do not run that comparison unless you tell them to, and most operators never do. The fix is structural: every pipeline needs a verification pass that re-queries the destination, counts what landed, and flags any gap before the run is marked done.

How do you debug a silent failure in n8n or Bubble.io?

Start with the count, not the run log. Pull the count of records the pipeline claims it wrote, then pull the count of records actually present in the destination, and look at the gap. If the destination is short, walk back through the pipeline stages and re-count at each one. The stage where the count first dropped is where the silent failure lives. From there, inspect the platform-specific failure modes: n8n Cloud's 60-second code-node timeout, Bubble's asynchronous list-field update behaviour, webhook acknowledgements that fire before persistence completes, dedup keys that include mutable fields. Never trust the platform's success message until you have re-queried reality.

What is the most common silent failure in n8n migrations?

Execution-cap timeouts. n8n Cloud caps each Code node at 60 seconds and each workflow at around 40 minutes (Pro). For long-running migrations that loop over thousands of records, hitting either cap silently truncates the batch. The pipeline reports success because the Code node returned what it had completed, the workflow finishes within the parent timeout, and the destination has the first N records but not the rest. The fix is the dispatcher and worker pattern: a parent workflow that chunks the input and triggers a child workflow per chunk via a webhook trigger, giving every chunk a fresh execution clock.

How do you make a Bubble.io data migration idempotent?

Hash a stable subset of the source record's fields into a deterministic fingerprint, store that fingerprint as a field on the destination row, and check for fingerprint existence before every write. If the fingerprint exists, update the existing row instead of inserting a new one. The fingerprint inputs must be stable over re-runs (do not include timestamps, auto-increment IDs, or fields the source mutates) and they must uniquely identify the logical record. Common patterns are name plus serial for hardware records, email plus tenant for users, and ISO date plus location for events. Idempotent pipelines can re-run from any failure point without producing duplicates.

How do you verify a pipeline run actually completed?

Re-query the destination database for the absolute count and compare it to the source. The pipeline's own success messages are not enough. Build a verification step into every production pipeline that runs after the main work and reports three numbers: source count, destination count after the run, and the delta. Treat any non-zero delta as a bug to investigate, not as expected drift. For relationships (parent-child links, list-field contents), run the verification against the parent side as well as the child side, because most platforms only update one direction reliably.