8 min readRishi

Dual-Write Dropped My Field and Reported Success

In this series: F&O and Dataverse integration

The dual-write health dashboard is green. The error list is empty. The customer that just saved in Finance & Operations shows up in Dataverse two seconds later — name, address, credit limit, all there. The custom hold-reason field you added last sprint is blank. Nobody got a failure email because, from the runtime's point of view, nothing failed.

This is the expensive kind of dual-write bug. A thrown error at least tells you which row to fix. A successful run that omitted a column will sit in production until a salesperson notices the form and a consultant swears the map is "definitely published."

I have walked this loop on customer, vendor, and released-product maps. The field is almost never "broken." It was never on the path the runtime actually uses.

Success means the mapped fields wrote

Dual-write does not copy a table. It copies the columns on the running table map, through the F&O data entity that map is bound to, into the Dataverse columns that map names.

If HoldReason is on CustTable and even on CustCustomerV3Entity in Visual Studio, that is not enough. Live sync will still skip it when any of these is true:

  • The entity field exists in metadata but is not mapped to a data source column.
  • The entity field is mapped, but the dual-write table map does not include it (or includes it on a draft you never published).
  • The Dataverse column does not exist in the environment the map is linked to.
  • Change tracking on the entity was initialized before the field existed, and the map was never bounced.

Unmapped columns are not errors. They are invisible. The runtime has no reason to complain.

Type mismatches are different: an enum mapped to a string, or a lookup mapped to a plain text column, usually fails the row. If the row is present and one column is empty, you are in the silent-omit case, not the type-mismatch case. Stop looking at the error log.

Walk the path in this order

Do not start in the dual-write UI. Start at the table and walk outward until the field disappears. That is the break.

1. The table has it, the entity does not

Confirm the field on CustTable (or whichever table actually stores it). Then open the entity the map uses — for customers that is typically a CustCustomerV3-family entity, not "whatever looks like Customer in the AOT."

The field must appear on the entity as a real field with a data source mapping. A display method, a form-only control, or a postLoad virtual field that you never mapped will not dual-write. Dual-write is not the form. It is the entity contract.

If you added the column through a table extension, you still need an entity extension and a field mapping. The compiler will happily build a project where the table has the field and the entity does not. The upgrade-safe version of that work is in extending data entities without breaking upgrades.

2. The entity has it, staging does not

Regenerate the staging table. Dual-write is not DMF, but the entity and its staging shape are still what the platform compiles together. Teams that skip Generate staging table (or a rebuild with that option) see the same symptom on imports: job succeeded, column empty.

Ship the package that contains the regenerated staging table. A field that exists in your dev AOT but not in the UAT binary is indistinguishable from "dual-write dropped it."

3. The entity has it, the running map does not

Open the running table map, not the Excel you edited three weeks ago and stored in a Teams chat.

Check all of:

  • The field is in the map, source entity field to Dataverse column.
  • You saved and published after adding it. Draft maps do not apply to live sync.
  • You are looking at the environment that is actually linked. Custom maps in an unmanaged solution in dev are not automatically in the linked Dataverse environment.

Microsoft out-of-box maps will not grow when you add a custom F&O field. You extend or clone the map. If you "added the field to the entity" and left the standard Customer V3 map untouched, dual-write is doing exactly what you asked: copying the Microsoft columns.

4. The map has it, change tracking still has yesterday's shape

Dual-write live sync depends on entity change tracking. Adding a field to an entity that is already running under a map is a schema change. The tracker that was initialized last month does not automatically start emitting the new column.

The recovery that actually works:

  1. Pause the table map (or stop it if your process requires a stop).
  2. Re-enable change tracking on the data entity in Data management, or re-initialize it if the UI offers that after a schema change.
  3. Publish the updated map.
  4. Start the map again.

Skipping the pause/start is how you get a green dashboard and a permanently empty column. The next customer update still syncs Name. HoldReason never joins the payload.

Do not "fix" this by running a full initial sync of 200,000 customers unless you mean to. For one new column, a targeted DMF push of the field, then live sync going forward, is the pattern that does not take the map down for a weekend. The broader dual-write sequencing and pause-around-batch advice is in dual-write pitfalls and patterns.

5. Dataverse never had a place to put it

The Dataverse column must exist in the linked environment, on the table the map writes (account, msdyn_vendor, and so on), with a type the map accepts. A column in an unmanaged solution that was never imported, a column on a different table, or a column named new_holdreason while the map still points at new_hold_reason will all present as success-plus-blank.

Publish customizations. Then open a record in a model-driven app and confirm the column is on the form if you expect users to see it — dual-write can fill a column that is not on the form, but "I don't see it" and "it is empty in the table" are different bugs. Check Advanced Find or a FetchXML query against the column, not only the form.

Prove it with one record

Pick a customer you can afford to touch in a non-prod legal entity.

  1. Set HoldReason in F&O to a value you will recognize (CREDIT-REVIEW-TEST).
  2. Save. Wait for dual-write lag on the health dashboard to return to near zero.
  3. Read the Dataverse row by alternate key / account number, not by scrolling a view.
  4. If the column is empty, dump the dual-write activity for that key. You want to see whether the outbound payload contained the field. If the payload omitted it, the break is on the F&O/entity/map/tracking side. If the payload contained it and Dataverse is still empty, the break is column name, plugin, or a workflow that clears the value on create/update.

That last case is common and is not a dual-write defect. A plugin on account Update that runs on dual-write inbound traffic can overwrite or skip fields. If you own that plugin, detect dual-write origin and return early for columns you do not master in Dataverse — the same pattern as in the pitfalls post.

A checklist I actually use before calling the map "done"

  • Field is on the table and on the entity the map names, with a data source mapping.
  • Staging table regenerated; package deployed to the environment that is linked.
  • Running table map lists the field; map is published, not draft.
  • Dataverse column exists in that environment; names match; type is compatible.
  • Map paused, change tracking re-initialized, map started after the schema change.
  • One record round-trips in non-prod; payload inspected if the column is still empty.
  • Plugins / flows on the Dataverse table do not blank the column on inbound dual-write.

If you cannot tick the first five, do not add retry policies, do not add a Power Automate "sync the field" flow, and do not run initial sync again. You will copy the same omission 200,000 times, still with a green dashboard.

When this is the wrong fight

If the field only needs to be visible in a model-driven app and nobody in Dataverse writes it, dual-write is the heavy tool. A virtual entity, or a business event plus a small write you own, will not spend a week on map versioning. The decision table is in choosing an F&O–Dataverse integration pattern.

Use dual-write when both sides must store the value and users will edit it in both apps. Then treat every new column as a contract change: entity, map, tracking, environment. Success in the dashboard only means the contract you published ran. It does not mean the column you cared about was on that contract.

Keep reading

Newsletter

New posts, straight to your inbox

One email per post. No spam, no tracking pixels, unsubscribe anytime.

Comments

  • No comments yet. Be the first.