Skip to content
All Posts
Migration Guide

Magento USPS AC-15210: Apply the Patch, Then Fix the Three Things It Leaves Broken

·Updated ·9 min read·By RevAddress·Migration Guide

AC-15210 is not a bug. It is the Adobe quality patch that moves the built-in Magento_Usps carrier off the USPS Web Tools XML API, which was retired on January 25, 2026, and onto the USPS REST APIs. If your Magento 2 store stopped showing USPS rates, the patch is the fix, and most stores can apply it in an afternoon.

The reason AC-15210 reads like a bug in search results is that its patch ID appears in the titles of the GitHub issues reporting what it gets wrong. Two of those are open, and between them they name four distinct problems: a config merge error the patch introduces, and three things it does not finish. Each has a workaround. This guide applies the patch, then works through all four.

What the patch covers

Adobe publishes AC-15210 through the Quality Patches Tool. Two facts decide whether it applies to you:

Affected versions Adobe Commerce and Magento Open Source 2.4.6-p3 through 2.4.8
Minimum tooling Quality Patches Tool 1.1.70

Adobe publishes the range as 2.4.6-p3 up to but not including 2.4.9, so a 2.4.9 install does not take this patch. Anything below 2.4.6-p3 needs a version upgrade first, because the patch will not apply cleanly.

Check where you stand:

Version and patch inventorybash
bin/magento --version
composer show magento/quality-patches
vendor/bin/magento-patches status

Apply it

Install the tooling and apply AC-15210bash
composer require magento/quality-patches --update-with-dependencies
vendor/bin/magento-patches apply AC-15210

bin/magento setup:upgrade
bin/magento setup:di:compile
bin/magento cache:flush

Confirm the patch registered before you touch anything else. vendor/bin/magento-patches status prints AC-15210 as applied; if it prints a conflict instead, you have a local modification to vendor/magento/module-usps and you need to resolve that first.

Fix the duplicate config node before it bites

The patch writes two sibling price_type nodes into vendor/magento/module-usps/etc/config.xmlEPS and COMMERCIAL. On a stock install nothing happens. The moment any custom module overrides USPS carrier configuration in its own config.xml, Magento’s DOM merger runs an XPath query against a path that now matches twice and cannot decide which node to write:

More than one node matching the query: /config/default/carriers/usps/price_type

Once that fires, every bin/magento command that loads configuration fails, which means you cannot clear cache, cannot compile, and cannot get to the admin to undo it. This is magento/magento2 issue 40779, still open.

The workaround is to delete the duplicate after applying the patch, leaving exactly one price_type element:

Inspect the merged node before editingbash
grep -n -A2 -B2 'price_type' vendor/magento/module-usps/etc/config.xml

A vendor/ edit is a patch you now own, so make it a real one. Add it to composer.json under extra.patches with cweagans/composer-patches, or the next composer install silently reinstates the duplicate and your build breaks in CI instead of on your laptop.

Configure the carrier

The credential fields changed shape entirely. Web Tools took a USERID in a query string. REST takes an OAuth client pair issued to an app you register at developers.usps.com.

Navigate to Stores → Settings → Configuration → Sales → Delivery Methods → USPS. The fields that matter after the patch:

Field What goes in it
Consumer Key From your USPS developer-portal app
Consumer Secret From the same app
Pricing Options Retail or Commercial, matching your USPS account
Mode Live for production, Development against the USPS test environment
Allowed Methods Re-select every service. See below
Debug On until rates render, then off
Displayed Error Message Reword it. The default tells the customer nothing

Then clear the config cache, because Magento reads carrier credentials from cache and a saved-but-uncached key looks exactly like a wrong key:

Clear config cache after saving credentialsbash
bin/magento cache:clean config

Re-select Allowed Methods even if the checkboxes look correct. Switching the carrier’s API mode does not preserve the previously configured method list, which is the second finding in magento/quality-patches issue 154. The stored value can be empty while the admin still renders the old selection. An empty Allowed Methods produces exactly the symptom you started with: no USPS options at checkout, no error anywhere.

The three gaps the patch leaves

Issue 154 is the practitioner’s list, and it is worth reading in full before you go live. The three findings, and what to do about each:

Tracking does not come back. The patch restores rating. It does not restore tracking detail, so My Orders → Track Order renders an empty panel where scan events used to appear. There is no configuration that turns it on. Either you write the tracking call yourself against GET /tracking/v3/tracking/{trackingNumber}, or you route tracking through a service that already implements it.

Allowed Methods is lost on an API switch. Covered above. Re-select and save.

429s are unhandled, and they look like nothing. USPS caps a registered application at 60 requests per hour across every endpoint it calls, not per endpoint. Exceed it and USPS returns:

429 — Exceeded quota limit. Please contact https://emailus.usps.com/s/web-tools-inquiry

The patched carrier does not surface that response. It returns no rates, Magento renders an empty shipping block, and the customer sees a checkout with nothing to select. No admin notice, no customer-visible error, nothing in the order record. The only place it appears is var/log/shipping.log with Debug on.

Do the arithmetic before you trust the cap. Sixty per hour is 1,440 a day shared across rating, tracking, and anything else on the same app. A store doing 50 orders a day fires a rate request on every cart update, not every order, so the real request count is a multiple of sessions rather than orders. Peak-hour traffic is where it breaks, which is also where it costs the most.

Watch for the quota response while testingbash
tail -f var/log/shipping.log | grep -iE '429|quota|unauthorized'

What the patch means for your USPS bill

Effective August 1, 2026, USPS moved the Addresses API onto a consumption-tier fee curve and gated access behind a signed license agreement. The patched carrier calls USPS directly from your store on your own Consumer Key, which means the license, the monthly fee, and the 60-per-hour cap are yours. Budget the signature time before your launch date; it moves at legal-department speed on both ends. The USPS API pricing breakdown has the published curve and the enrolment sequence.

The rate limit is the part that surprises people, because it did not exist under Web Tools. Nothing in the patch caches, budgets, or retries against it.

If you want caching and tracking in front of USPS

There is no drop-in Magento module for this from us, and a store that only needs rates should stop at the patch. If you also need tracking detail and a cache between your checkout and the USPS cap, a small custom carrier under your own namespace is about a hundred lines and it survives every Magento security release, because it touches no core code.

The carrier extends AbstractCarrier and posts to the RevAddress API, which holds your connected USPS license, manages the OAuth lifecycle, caches identical quotes, and budgets against the 60-per-hour window:

app/code/YourCo/UspsRates/Model/Carrier.php — rate collectionphp
<?php

namespace YourCo\UspsRates\Model;

use Magento\Quote\Model\Quote\Address\RateRequest;
use Magento\Shipping\Model\Carrier\AbstractCarrier;
use Magento\Shipping\Model\Carrier\CarrierInterface;
use Magento\Shipping\Model\Rate\Result;

class Carrier extends AbstractCarrier implements CarrierInterface
{
  protected $_code = 'yourco_usps';

  public function getAllowedMethods(): array
  {
      return ['USPS_GROUND_ADVANTAGE' => 'USPS Ground Advantage'];
  }

  public function collectRates(RateRequest $request): Result|bool
  {
      if (!$this->getConfigFlag('active')) {
          return false;
      }

      $payload = [
          'originZIPCode'      => $this->getConfigData('origin_zip'),
          'destinationZIPCode' => $request->getDestPostcode(),
          // USPS Domestic Prices takes weight in POUNDS as a decimal.
          'weight'             => round((float) $request->getPackageWeight(), 2),
          'length'             => 12,
          'width'              => 9,
          'height'             => 3,
      ];

      $response = $this->httpClient->post('/api/rates', $payload);
      $result   = $this->rateResultFactory->create();
      $labels   = $this->getAllowedMethods();

      // USPS Domestic Prices is returned verbatim under "rates"; the quotes
      // are in rates.rateOptions, each carrying mailClass and totalBasePrice.
      foreach ($response['rates']['rateOptions'] ?? [] as $option) {
          $mailClass = $option['mailClass'];

          $method = $this->rateMethodFactory->create();
          $method->setCarrier($this->_code);
          $method->setCarrierTitle($this->getConfigData('title'));
          $method->setMethod($mailClass);
          $method->setMethodTitle($labels[$mailClass] ?? $mailClass);
          $method->setPrice($option['totalBasePrice']);
          $method->setCost($option['totalBasePrice']);
          $result->append($method);
      }

      return $result;
  }
}

Three things in that snippet are the ones people get wrong. collectRates returns false, not null, when the carrier is off, because Magento treats a null return as a carrier error and can suppress the whole shipping block. USPS Domestic Prices sends no human-readable service name, only a mailClass enum, so the label has to come from your own map or checkout shows customers the string USPS_GROUND_ADVANTAGE. And weight goes in pounds as a decimal, so a store configured in kilograms converts before the call, not inside the response mapping.

Tracking is the same shape against GET /api/tracking/{trackingNumber}, mapped into what Magento’s getTracking() expects.

Rates and tracking run on the USPS license you connect once from the dashboard, not on ours. Connect it and those endpoints return live USPS pricing; leave it unconnected on a paid plan and the response carries the header X-RevAddress-BYOK: required and a _byok block naming the reason. That header is the first thing to check when a call succeeds but returns nothing useful.

Address standardization, geocoding, and address extract run against Census Bureau data with no USPS license involved and no per-lookup fee, which covers getting a shipping address into correct postal form. The full USPS suite — DPV, ZIP+4, rates, service standards, and tracking — runs on your connected license, with the OAuth lifecycle, caching, retries, and rate-limit budgeting handled on our side.

Before you call it done

Do all five. Each one has failed silently for somebody.

  1. Rates render at checkout for a domestic ZIP you know is deliverable, with Debug off.
  2. var/log/shipping.log shows no 401, no 429, and no invalid_client across a dozen quotes.
  3. A bin/magento cache:flush completes without the XPath merge error, run from a store that has your custom module installed.
  4. Allowed Methods survives a save, a cache flush, and a page reload.
  5. A historical order placed under the old usps carrier code still renders its carrier name in the admin grid. Magento reads sales_order.shipping_description, a stored string, so this holds as long as you disabled Magento_Usps rather than removing it.

If you replaced the built-in carrier, disable it rather than deleting it, and check first whether you have orders to protect:

Count orders on the legacy carrier codebash
mysql -u magento -p magento -e \
"SELECT COUNT(*) AS order_count FROM sales_order WHERE shipping_method LIKE 'usps_%';"

bin/magento module:disable Magento_Usps
bin/magento setup:upgrade
bin/magento cache:clean

Sources

Patch scope and version range: Adobe’s AC-15210 entry in Quality Patches Tool 1.1.70. The three post-patch gaps: magento/quality-patches issue 154. The config merge error and its workaround: magento/magento2 issue 40779. USPS fee and license terms: the USPS Addresses API Tech Sheet, summarized in what the USPS API actually costs.

Start here

  • What the USPS API actually costs — the published fee curve and the license sequence to start before your launch date
  • Get a free API key — standardization, geocoding, and address extract run on Census Bureau data at no cost and no USPS license
  • Plans and BYOK — rates, tracking, and DPV run on the USPS license you connect, never as a flat-rate inclusion
  • USPS OAuth troubleshooting — for the 401s and invalid_client errors the new credential pair produces

Questions

Is AC-15210 a bug or a fix?
A fix. AC-15210 is the Adobe quality patch that moves the built-in Magento_Usps carrier off the retired Web Tools XML API and onto the USPS REST APIs. It ships in Quality Patches Tool 1.1.70 and later and applies to Adobe Commerce and Magento Open Source 2.4.6-p3 through 2.4.8. The confusion comes from the patch ID appearing in GitHub issue titles that report problems with it.
Why are USPS rates still missing after I applied AC-15210?
Three usual causes. The Consumer Key and Consumer Secret are not saved under Stores > Settings > Configuration > Sales > Delivery Methods > USPS. Allowed Methods was silently emptied when the carrier switched API mode. Or USPS is returning 429 and the patched carrier does not surface it, so the rate block renders empty rather than showing an error.
What is the "More than one node matching the query" error after patching?
AC-15210 writes two sibling price_type nodes into vendor/magento/module-usps/etc/config.xml. Any custom module that overrides USPS carrier config triggers an XPath merge against a duplicated path, and every bin/magento command that loads configuration then fails. It is tracked as magento/magento2 issue 40779. Removing the duplicate node restores the merge.
Does the patched carrier need a USPS license now?
Yes. Since August 1, 2026, USPS bills the Addresses API on a consumption-tier curve and gates access behind a signed license agreement. The patched carrier calls USPS directly on your store's own Consumer Key, so the license, the fee, and the 60-requests-per-hour application cap are all yours to hold.
Should I remove Magento_Usps instead of patching it?
No. Disable it only if you are replacing it with your own carrier, and disable rather than remove, so historical orders that reference the usps carrier code keep rendering in the admin. Removing the module drops schema entries those orders read.