Mage2Plenty v3.15 - Rate-Limit Resilience, Stable Order Collection & Accurate Price Sync
Mage2Plenty v3.15 is a reliability release built from three live client incidents. The headline: the API client now respects PlentyONE's throttling contract - it honours the Retry-After header and backs off instead of failing the moment a rate limit is hit, which was stranding high-volume stock corrections against PlentyONE's per-minute write limit. Alongside it, order collection no longer aborts with "Invalid data for insert" when a batch mixes orders that have an external order reference with ones that don't, removing a special price in PlentyONE now removes it in Magento (opt-in), and two CLI commands stop breaking bin/magento list.
Rate-limit resilience: honour Retry-After, back off on 429
PlentyONE throttles the REST API per user, per minute - separately for reads and writes (writes are the tighter budget). A client running frequent stock corrections kept hitting the write limit and, instead of waiting the one second PlentyONE asked for, the connector failed the call outright: Access to PlentyONE REST API system is throttled. and short period write limit reached.
The retry logic was there, but wired to the wrong signal. It computed its wait from the period decay counter, which reads 0 at the exact moment a window resets - precisely when a throttled call happens - so the "wait and retry" branch never ran, even though the response's Retry-After header clearly said wait 1 second. On top of that, per-route and write-specific limits return a 429 that the header-based detection didn't always recognise, so those fell through as hard errors with no retry at all.
v3.15 completes the throttling contract:
Retry-Afteris now honoured as the primary backoff signal, falling back to the period decay only when the header is absent.- Any HTTP 429 is treated as throttling - a safety net that catches per-route and write-specific limits the header checks miss - and is retried rather than failed.
- Retries are bounded, so a persistently saturated limit surfaces as an error instead of looping forever.
The result: rate-limited requests now pause for the interval PlentyONE specifies and succeed on retry, exactly as the connector was always meant to behave.
Order collection: no more "Invalid data for insert"
Collecting orders from PlentyONE could abort an entire batch with Invalid data for insert. The cause was a subtle data-shape mismatch: the connector added the external_order_id column to a collected row only for orders that actually had an external reference. A batch that mixed orders with and without one produced rows with inconsistent columns, and the bulk insert - which derives its column list from the first row - rejected the batch.
v3.15 always includes external_order_id in every collected row (null when absent) so the batch is column-consistent, while still preserving a stored external reference on re-collection (a null incoming value never overwrites an existing one). Order import runs cleanly regardless of the order mix in each batch.
Accurate price sync: removed special prices are cleared
Previously, when a special price was removed in PlentyONE, it lingered on the Magento product - the import only ever wrote mapped prices, never cleared one that had disappeared upstream. v3.15 adds a "Delete Product Price Difference" toggle to the item import profile: with it enabled, a mapped price that no longer exists in PlentyONE is removed from the Magento product on the next import. The base price is never cleared, so a product can't be left without a price, and the option is off by default - installs that intentionally keep Magento-side prices are unaffected.
CLI: commands stop breaking bin/magento list
Two console commands - plenty:order:validate and plenty:profile:status - each defined their own --verbose option, which collides with Symfony's built-in global --verbose/-v. The collision surfaced as An option named "verbose" already exists. and broke bin/magento list (used by some deployment tooling). Both commands now use Symfony's built-in output verbosity, so -v/--verbose behave exactly as before, without the clash.
Release Summary
| Module | Version | Bump | Key Changes |
|---|---|---|---|
module-plenty-client | 2.1.7 → 2.1.8 | patch | Honour Retry-After; treat any HTTP 429 as throttling; bounded retries |
module-plenty-item-profile | 3.6.2 → 3.7.0 | minor | Clear a mapped price on import when removed in PlentyONE (toggle-gated) |
module-plenty-order-profile | 2.11.0 → 2.11.1 | patch | Fix order-collect "Invalid data for insert"; drop conflicting --verbose option from plenty:order:validate |
module-plenty-profile | 2.2.2 → 2.2.3 | patch | Drop conflicting --verbose option from plenty:profile:status |
Metapackage: softcommerce/mage2plenty-os 3.14.0 → 3.15.0
Upgrade Guide
composer require softcommerce/mage2plenty-os:^3.15
bin/magento setup:upgrade
bin/magento setup:di:compile
bin/magento cache:flush
No schema changes in this release. Behaviours worth noting on upgrade:
- Throttled calls now wait and retry instead of failing - expect fewer rate-limit errors in the logs and a brief pause under heavy write load rather than a hard failure.
- A new import option, "Delete Product Price Difference", is off by default. Enable it (Item Import profile → price settings) only if you want prices removed in PlentyONE to be cleared in Magento.
plenty:order:validateandplenty:profile:statuskeep-v/--verboseworking via Symfony's built-in verbosity - no change to how you call them.
Resources
- Stock Export Profile guide
- Order Import Profile guide
- Product Import Profile guide
- Complete Changelog
Questions about the upgrade? Reach out to us at support@byte8.io.
