Most NetSuite ↔ WooCommerce integrations don’t fail in the first week. They fail in month two — when traffic doubles, a new SKU type lands, or a SuiteScript deploy quietly bumps a field name. The first week’s smoke test catches none of it.
Here are the seven failure modes we see most often, in the order they show up.
1. Token refresh fails silently
- Symptom: works for 59 minutes, then every request returns 401
- Root cause: code caches the access token in memory on a timer instead of on response
- Fix: refresh on every 401, not on a clock. Treat the token cache as a hint, not a source of truth.
2. The 429 rate-limit avalanche
- Symptom: half your inventory updates fail during a flash sale
- Root cause: too much concurrency; naive immediate retries make it worse
- Fix:
- Respect the
Retry-Afterheader - Cap concurrency to your account’s documented limit (it’s lower than you think)
- Queue writes through one in-process limiter so cron jobs don’t race
- Respect the
3. Sandbox/production credential cross-wiring
- Symptom: test orders show up in live NetSuite (or staging breaks against prod data)
- Root cause: a developer copied credentials into the wrong
.env - Fix:
- Name env vars by environment:
NS_PROD_CLIENT_ID, notNS_CLIENT_ID - Refuse to boot if the account ID in the credentials doesn’t match the expected environment
- Name env vars by environment:
4. Duplicate orders on webhook retry
- Symptom: one WooCommerce order becomes two Sales Orders in NetSuite
- Root cause: NetSuite was slow, your webhook handler timed out at 30s, WooCommerce retried — but you created a new SO instead of finding the existing one
- Fix:
- Store the WooCommerce order ID in a custom field on the Sales Order
- Search by that field before creating. If found, return the existing record ID.
This is the highest-ROI piece of code in any integration and the one most often missing.
5. Inventory drift on partial fulfillment
- Symptom: the store thinks 100 are available; NetSuite thinks 100 are committed — same units, counted twice
- Root cause: WooCommerce decrements on order placement, NetSuite decrements on ship; you sync on-hand instead of available
- Fix:
- Sync available (on-hand minus committed) from NetSuite, not on-hand
- Disable WooCommerce’s own decrement for SKUs that NetSuite owns
- Pick one source of truth per field, every time
6. SuiteScript deploys that rename fields
- Symptom: writes start failing with
USER_ERRORthe morning after an admin shipped a SuiteScript update - Root cause: a custom field’s script ID changed, or a new mandatory field appeared; your integration was hardcoded
- Fix:
- Schedule a weekly job that diffs the live custom-field list against a checked-in fixture
- Page the integration owner when the diff is non-empty — in office hours, before the next deploy
7. The reconciliation report nobody wants to build
We write about this in every post because it matters every time. A daily diff of three numbers between NetSuite and WooCommerce:
- Total item count
- Total on-hand inventory value
- Total open order value
Email it to the integration owner each morning. The day the numbers drift is the day you find a bug — and finding it the same day is the entire difference between a fixable issue and a quarter-close nightmare.
The pattern underneath all seven
None of these failures are about whether your code is “correct” in a unit-test sense. They’re about behavior under conditions you didn’t see in week one:
- Load
- Retries
- Partial state
- Schema drift
- Environment confusion
Integrations that survive year two aren’t smarter — they assumed they’d be wrong sometimes and built the recovery in early.
If you’d rather not write all of that yourself
- NetSuite Integration Basic — auth, idempotency, inventory direction
- NetSuite Integration Pro — adds rate-limit queue, schema-drift watchers, reconciliation report
- Already hit one of these in production? Thirty-minute call — we’ve probably debugged that exact failure for someone else this quarter.