1import{j as e}from"./vendor-motion-CfBPC-kS.js";import{L as t}from"./vendor-react-BR_v4Ont.js";import{C as n}from"./ContentPage-BxMYAaUt.js";import"./index-VVKdn61F.js";import"./vendor-supabase-BSd6ADfF.js";import"./HireCTA-DktS03tb.js";import"./EmailGateModal-BCI8ZqlV.js";import"./dialog-my3VBzDi.js";const r=e.jsx(e.Fragment,{children:e.jsxs(e.Fragment,{children:[e.jsx("h2",{children:"Symptoms â what you're seeing"}),e.jsxs("ul",{children:[e.jsx("li",{children:"The n8n execution is green and every node shows success, but the destination has fewer records than the source"}),e.jsx("li",{children:"A retry or manual re-run creates duplicate CRM records instead of updating the existing record"}),e.jsx("li",{children:"The record exists in the destination, but one or more fields contain the wrong value or a value from another item"}),e.jsx("li",{children:"Source and destination counts match, but users still report incorrect pipeline stages, owners, tags, or contact details"}),e.jsx("li",{children:"The workflow works in a small test but loses or corrupts records during a larger batch"})]}),e.jsxs("p",{children:[e.jsx("strong",{children:"Last updated:"})," 2026-06-08 · ",e.jsx("strong",{children:"Tested on:"})," n8n 1.x · ",e.jsx("strong",{children:"Read time:"})," 6 min"]}),e.jsx("h2",{children:"Why this happens"}),e.jsx("p",{children:"n8n execution success is transport-level information. It tells you that a node completed without returning an error. It does not tell you that the destination now matches the source."}),e.jsx("p",{children:"A destination API can return a successful response while accepting only part of a payload, normalizing a value, writing to the wrong record, or ignoring a field it does not support. A workflow can also lose correctness between nodes when an item is filtered, merged incorrectly, or mapped against the wrong item."}),e.jsx("p",{children:"The most common production causes are:"}),e.jsxs("ul",{children:[e.jsxs("li",{children:[e.jsx("strong",{children:"No stable correlation key."})," The workflow creates a destination record but does not preserve the source record ID. After a retry, it cannot tell whether it should update or create."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Non-idempotent writes."})," A timeout after the destination accepted a create request looks like a failed request to n8n. Retrying the item creates a second record."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Partial batch handling."})," A Split Out, Loop Over Items, filter, or merge path processes fewer items than the source query returned. The execution can still finish successfully."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Incorrect item mapping."})," An expression reads from the current item when it should read from a paired item, or a Code node returns only the first item instead of the complete collection."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"No post-write verification."})," The workflow checks the HTTP status but never reads the destination record back or compares the important fields."]})]}),e.jsxs("p",{children:["If an external API call is failing before it can write, diagnose that separately. For example, an ",e.jsx(t,{to:"/fix/n8n-http-401-unauthorized-ghl",style:{color:"var(--tm-accent)"},children:"n8n HTTP 401 error when calling GoHighLevel"})," is an authentication problem, not a reconciliation problem. If the API is rate limiting a batch, use the ",e.jsx(t,{to:"/fix/n8n-openai-rate-limit-429",style:{color:"var(--tm-accent)"},children:"batch and wait pattern for n8n 429 errors"})," before judging the destination state."]}),e.jsx("h2",{children:"The fix, step by step"}),e.jsxs("ol",{children:[e.jsxs("li",{children:[e.jsx("strong",{children:"Choose the source record ID as the correlation key."})," Add the immutable ID from the source system to the destination record, either in a dedicated external-ID field or in a custom field. Do not use a name, email address, or array position as the primary identity unless the source system guarantees it is unique and immutable."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Record the expected write before making the API call."})," Keep the source ID, destination lookup key, and important mapped values together as the item moves through the workflow. This gives the later verification step the exact expected state rather than forcing it to reconstruct the mapping."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Make the destination write idempotent."})," Prefer the destination API's upsert or update-by-external-ID operation. If the API does not support that, use an HTTP Request or native node lookup by the source ID, then branch to update when a re
1cord exists and create when it does not. Store the returned destination ID for the verification step."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Verify the response, not only the status code."})," After the write, check that the response contains the destination ID and the source correlation key. A 200 or 201 response without the expected identity is not sufficient evidence that the intended record was written."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Read the destination back."})," Use the destination's Get, Search, or HTTP Request operation to fetch the record by the stored destination ID or source correlation key. Compare the fields that matter to the business outcome: for example, email, pipeline stage, owner ID, amount, or appointment time. Do not compare only the record count."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Validate every item."}),' If you use a Code node in "Run Once for All Items" mode, iterate over the complete input collection. Do not access only item zero. The following pattern separates missing records from field mismatches; replace the marked field names with the fields returned by your source and destination APIs:']})]}),e.jsx("pre",{style:{background:"var(--tm-code-bg)",color:"var(--tm-code-text)",padding:16,borderRadius:4,marginTop:8,fontSize:13,overflowX:"auto"},children:`// Code node: Run Once for All Items 2// Input: one item per source record after destination read-back. 3// Replace sourceId, destinationId, and the expected/actual field names below with your own. 4const results = $input.all().map((item) => { 5 const source = item.json.source; 6 const destination = item.json.destination; 7 8 if (!destination) { 9 return { 10 json: { 11 sourceId: source.sourceId, 12 status: "missing", 13 }, 14 }; 15 } 16 17 const mismatches = []; 18 for (const field of ["email", "ownerId", "stageId"]) { 19 if (source[field] !== destination[field]) { 20 mismatches.push({ 21 field, 22 expected: source[field], 23 actual: destination[field], 24 }); 25 } 26 } 27 28 return { 29 json: { 30 sourceId: source.sourceId, 31 destinationId: destination.destinationId, 32 status: mismatches.length ? "mismatch" : "verified", 33 mismatches, 34 }, 35 }; 36}); 37 38return results;`}),e.jsxs("p",{style:{marginTop:32},children:[e.jsx("strong",{children:"Need another pair of eyes before this causes more bad records?"})," ",e.jsx(t,{to:"/contact#booking",style:{color:"var(--tm-accent)"},children:"Book a 30-min scope call"}),". I'll inspect the workflow, the source and destination APIs, and the reconciliation key. You can also ",e.jsx("a",{href:"mailto:[email protected]",style:{color:"var(--tm-accent)"},children:"email Basit"})," with an execution ID and a redacted sample."]}),e.jsxs("ol",{start:7,children:[e.jsxs("li",{children:[e.jsx("strong",{children:"Persist the verification result."})," Send missing records and mismatches to an error table, a Supabase table, a Google Sheet used for operations, or another durable store. Include the source ID, destination ID, execution ID, error type, and first-seen timestamp. An alert without a durable mismatch record is difficult to investigate after the fact."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Reconcile on a schedule."})," Add a separate Schedule Trigger workflow that selects a boun
38ded source window, fetches the corresponding destination records, compares IDs and important fields, and reports the differences. Run it after the normal sync and periodically outside the sync window. Keep the window and comparison key explicit so a large client account does not require an unbounded full-table scan on every run."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Alert on verified business failures."})," Alert when a record is missing, duplicated, or mismatched after read-back. Do not alert only when an n8n node throws an exception. If the source contains 50 records and verification returns 47 verified, one missing, one duplicate, and one mismatch, the workflow must report those three categories separately."]})]}),e.jsx("h2",{children:"Common variations"}),e.jsxs("ul",{children:[e.jsxs("li",{children:[e.jsx("strong",{children:'"The counts match but the records are wrong"'})," â count comparison is too weak. Compare stable IDs and a defined set of business-critical fields after the write."]}),e.jsxs("li",{children:[e.jsx("strong",{children:'"The workflow duplicates records after a timeout"'})," â the destination may have accepted the request before n8n received the response. Use an external ID or upsert so a retry resolves to the same destination record."]}),e.jsxs("li",{children:[e.jsx("strong",{children:'"Only the last item has the right values"'})," â inspect the Code node mode and item handling. A Run Once for All Items node must return an array containing a result for every input item."]}),e.jsxs("li",{children:[e.jsx("strong",{children:'"The nightly sync misses records created during the run"'})," â use a watermark such as a source updated-at value, overlap the next query window, and reconcile the overlap by source ID. A schedule trigger's timezone also affects when that window runs; check the ",e.jsx(t,{to:"/fix/n8n-schedule-trigger-timezone",style:{color:"var(--tm-accent)"},children:"n8n schedule trigger timezone fix"})," if the job starts at the wrong local time."]}),e.jsxs("li",{children:[e.jsx("strong",{children:'"The destination API reports success but silently ignores a field"'})," â read the record back and compare the API's returned representation. Confirm the field name, data type, permissions, and whether the destination allows that field to be updated."]})]}),e.jsx("h2",{children:"When this isn't the issue"}),e.jsx("p",{children:"If reconciliation shows that every source ID and field matches but users still see the wrong business state, move the investigation downstream:"}),e.jsxs("ul",{children:[e.jsx("li",{children:"Check destination-side automations, workflows, triggers, and queues that may change the record after n8n writes it"}),e.jsx("li",{children:"Check whether the destination UI is showing a cached view, a different sub-account, workspace, pipeline, or environment"}),e.jsx("li",{children:"Check timezone conversion and date serialization when the mismatch is limited to appointments, scheduled tasks, or time-based status changes"}),e.jsx("li",{children:"Check permissions and field visibility if the API response contains the value but the user interface does not display it"})]}),e.jsx("p",{children:'The reliable pattern is not "the execution is green, so the sync worked." It is: preserve the source ID, make the write idempotent, read the destination back, compare the fields that matter, and reconcile again on a schedule.'}),e.jsx("p",{style:{marginTop:32},children:e.jsx("strong",{children:"Related pages"})}),e.jsxs("ul",{children:[e.jsx("li",{children:e.jsx(t,{to:"/fix/n8n-openai-rate-limit-429",style:{color:"var(--tm-accent)"},children:"Fix n8n OpenAI 429 rate limit errors"})}),e.jsx("li",{children:e.jsx(t,{to:"/fix/n8n-http-401-unauthorized-ghl",style:{color:"var(--tm-accent)"},children:"Fix n8n HTTP 401 unauthorized errors with GoHighLevel"})}),e.jsx("li",{children:e.jsx(t,{to:"/fix/n8n-schedule-trigger-timezone",style:{color:"var(--tm-accent)"},children:"Fix n8n schedule trigger timezone problems"})})]}),e.jsxs("p",{style:{marginTop:32},children:[e.jsx("strong",{children:"Stuck on this for more than 30 minutes?"})," ",e.jsx(t,{to:"/contact#booking",style:{color:"var(--tm-accent)"},children:"Book a 30-min scope call"}),". I'll get on Zoom, look at your workflow, and either fix it on the call or scope what it'll take. No upsell, no decks. You can also ",e.jsx("a",{href:"mailto:[email protected]",style:{color:"var(--tm-accent)"},children:"email Basit"})," with an execution ID and a redacted sample."]})]})}),u=()=>e.jsx(n,{tier:1,slug:"n8n-destination-data-wrong-despite-success",title:"Fix: n8n shows success but destination data is wrong â basitq.cloud",subtitle:"n8n reports that every node completed, but execution success does not prove the destination state is correct. Preserve source IDs, make writes idempotent, read back the destination, and reconcile the full batch.",metaDescription:"Fix n8n workflows that show success but create missing, duplicate, or incorrect destination records. Use source IDs, read-back checks, and reconciliation in production.",lastUpdated:"2026-09-21",readTime:"6",techStack:["n8n","HTTP Request","Code node","CRM API","Reconciliation"],relatedPages:[{title:"N8N Schedule Trigger Timezone",slug:"n8n-schedule-trigger-timezone",tier:1},{title:"N8N Openai Rate Limit 429",slug:"n8n-openai-rate-limit-429",tier:1},{title:"N8N Http 401 Unauthorized Ghl",slug:"n8n-http-401-unauthorized-ghl",tier:1}],relatedServices:[],faq:[{q:"Why does n8n show success when records are missing in the destination?",a:"Execution success means the nodes returned without an error. It does not verify that every source record was written, that the destination accepted the intended fields, or that a later retry did not create a duplicate."},{q:"Is comparing source and destination counts enough to verify an n8n workflow?",a:"No. Counts detect some missing or duplicate records, but they do not detect a wrong field mapping, an update written to the wrong record, or two records with the same destination value. Compare stable source IDs and important fields."},{q:"How do I stop n8n retries from creating duplicate records?",a:"Use a stable source-system ID as an idempotency key. Look up the destination record by that key before creating it, or use the destination API's upsert or external-ID operation when it supports one."},{q:"Should I use a separate n8n workflow for reconciliation?",a:"Yes for recurring production integrations. Keep the write workflow focused on delivery, then run a scheduled reconciliation workflow that compares s
38ource and destination records, records mismatches, and alerts on unresolved differences."}],body:r});export{u as default};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.