[FROSTLABS] · home / writing / lead-time wipe bug
2026-03-15 · 6-min read · Amazon SP-API · Odoo · FBM

The Odoo + Amazon lead-time-to-ship wipe bug across FBM listings.

A recurring bug in Odoo + Amazon FBM integrations: every fulfillment-side PATCH to a listing silently wipes the lead_time_to_ship_max_days field, dropping the shipping window to Amazon's default (typically 1-2 days). Discovered across 6,838 active FBM listings on a 7-month engagement. If your made-to-order business is on FBM and your fulfillment integration touches Amazon listings at order time, this bug is probably running on your catalog right now.

$The symptom

On Amazon FBM (Fulfilled-By-Merchant), the lead_time_to_ship_max_days attribute on each listing tells Amazon how long after order placement the customer should expect the item to ship. For standard inventory: 1-2 days. For made-to-order businesses: 5, 10, 14, or even 21 days depending on the product.

If lead_time_to_ship_max_days is wrong (specifically, if it's lower than reality), three bad things happen:

On the engagement, the client's late-shipment rate had been creeping up for months. Buy Box win rates had been declining. Nobody had connected those two facts to a specific cause. The cause, when I found it, was a wipe-on-every-fulfillment-PATCH that had silently zeroed the lead-time field across the entire active FBM catalog.

$The bug

Most Odoo + Amazon connectors push listing updates via Amazon's SP-API Listings endpoint:

PUT /listings/2021-08-01/items/{seller}/{sku}
{
  "productType": "...",
  "attributes": {
    "lead_time_to_ship_max_days": [{"value": 10, "marketplace_id": "..."}],
    "fulfillment_availability": [{...}],
    ...
  }
}

That's the full-listing PUT. Every attribute you want on the listing must be in the payload. If you omit an attribute, Amazon's behavior depends on whether you're using PUT or PATCH:

The bug is in connectors that use PATCH but treat the listing payload as "what the integration knows about this listing right now." When the fulfillment integration fires (because an order was packed, or because inventory changed, or because tracking was updated), it constructs a PATCH that includes the fields the fulfillment system cares about, but omits the fields that other systems own.

For PATCH semantics, this is correct: omitted fields stay as-is. Amazon's SP-API, however, sometimes treats specific listing attributes as "all-or-nothing", and lead_time_to_ship_max_days is one of them. If the PATCH includes fulfillment_availability but not lead_time_to_ship_max_days, and the field has been previously set, the PATCH treats the omission as "unset this field." The listing's lead-time reverts to Amazon's default (often 2 days).

The behavior is inconsistent and not documented as such. From the SP-API side, the call returns 200 OK. The processing report shows success. The only way to detect the wipe is to GET the listing afterward and compare the attribute to what you sent.

$How to detect it on your catalog

Pull the current state of all your FBM listings, look at lead_time_to_ship_max_days, group by value:

from collections import Counter

def audit_fbm_lead_times(sp_client, seller_id, marketplace_id):
    counter = Counter()
    listings = []
    for sku in get_active_fbm_skus(seller_id):
        r = sp_client.get(
            f"/listings/2021-08-01/items/{seller_id}/{sku}",
            params={"marketplaceIds": marketplace_id, "includedData": "attributes"},
        )
        attrs = r.json().get("attributes", {})
        lt = attrs.get("lead_time_to_ship_max_days", [])
        value = lt[0].get("value") if lt else None
        counter[value] += 1
        listings.append({"sku": sku, "lead_time": value})

    # Compare to expected. If your business is MTO with 7-14 day lead times,
    # a cluster at 1 or 2 days is the wipe symptom.
    for v, n in counter.most_common():
        print(f"lead_time={v} days: {n} listings")
    return listings

What you want to see: distribution matching your actual production timeline. A made-to-order business should have most listings at 7-14 days. A standard-inventory business should have most listings at 1-3 days.

What raises a flag: a giant cluster at 1 or 2 days that shouldn't be there. If your products legitimately ship in 1-2 days, no bug. If your business is MTO and you're seeing 90%+ of listings at 1-2 days, you have the wipe.

On the prior engagement: out of 7,000 active FBM listings, 6,838 had been zeroed to 1 or 2 days. The remaining 162 were either correctly set or had been re-pushed by a different code path recently.

$The fix

Two steps. First, re-push correct lead times across the active set. Second, fix the integration so the wipe stops happening.

Step 1: re-push correct lead times

For each affected listing, compute the correct lead time from your Odoo data (typically product.template.sale_delay or a per-SKU override), then issue a focused PATCH that includes ONLY the lead-time field:

def repush_lead_time(sp_client, seller_id, sku, lead_time_days, marketplace_id):
    payload = {
        "productType": get_product_type(sku),  # required on every Listings PATCH
        "patches": [{
            "op": "replace",
            "path": "/attributes/lead_time_to_ship_max_days",
            "value": [{
                "value": lead_time_days,
                "marketplace_id": marketplace_id,
            }],
        }],
    }
    r = sp_client.patch(
        f"/listings/2021-08-01/items/{seller_id}/{sku}",
        params={"marketplaceIds": marketplace_id},
        json=payload,
    )
    return r.json()

The "op": "replace" on a JSON-Pointer path is the JSON Patch idiom that SP-API's Listings PATCH supports. Single attribute, single operation, clean.

Wait 5-10 minutes after the batch completes, then GET each listing back and verify the field is set correctly. If anything still reads wrong, retry. Amazon's processing is async; the field doesn't update instantly.

Step 2: fix the fulfillment integration

The harder part. The wipe is happening because the fulfillment code path doesn't preserve fields it doesn't own. Two patterns to fix it:

The narrow-PATCH pattern is cleaner. The Listings API's JSON Patch support is exactly designed for this. Most off-the-shelf connectors don't use it; they construct full-attribute payloads and rely on PATCH semantics to leave omitted fields alone. That's the assumption Amazon's API quietly violates for fields like lead-time.

$Specific to MTO businesses

If you're a made-to-order operator on Odoo + Amazon FBM, the wipe is especially expensive. Made-to-order businesses use elevated lead times (7-14 days typically) precisely because they can't ship in 1-2 days. When the wipe drops your lead time to Amazon's 2-day default:

The pattern compounds because the wipe re-fires every time the fulfillment integration touches a listing, which on an active FBM catalog is multiple times per day per SKU. Re-pushing lead times once isn't enough; the fix has to be at the integration level, otherwise the field gets wiped again within hours of the re-push.

On the prior engagement, this single bug was the largest silent revenue driver I found. Closing it (re-push + integration fix) returned the catalog to its actual production timeline and gave the LSR a path back to compliance.

$The transferable diagnostic

The pattern (Amazon attribute that gets silently wiped by a connector's PATCH that omits it) applies to more than just lead-time. Other fields that exhibit this behavior intermittently:

The diagnostic is the same in each case: GET the listing, group by the field, look for unexpected distributions. If your catalog has a giant cluster at a value that shouldn't be there, the wipe is running.

By David H. Frost · Frost Labs LLC More writing · Home · Privacy