All posts
Forward Development

Medusa 2.18 broke order export: the shipping method version trap

Order exports in Medusa 2.18.0 and 2.19.0 fail silently on any order with a shipping method. How to confirm it and what to do until it's fixed.

  • Medusa.js
  • Migrations
  • Operations

Someone in finance clicks Export on the admin Orders page. The UI says they'll be notified when the export is complete. No notification arrives. Not a success notification, not a failure notification — nothing. They wait, assume it's a big job, come back after lunch, click Export again. Still nothing. Eventually it escalates to you as "exports are slow," which is the wrong diagnosis, because the export isn't slow. It died on its first page of orders and told no one.

This is a real regression in @medusajs/medusa, introduced in 2.18.0 and still present in 2.19.0. The relevant file is byte-identical between those two releases, so upgrading from 2.18.0 to 2.19.0 does not fix it. 2.17.2 works.

The error is in your server logs, not in the admin

The admin gives you nothing, so go straight to the application logs. You're looking for the export-orders workflow failing on invoke:

error: export-orders:export-orders:invoke - Shipping method version is required to load adjustments
Error: Shipping method version is required to load adjustments
    at @medusajs/order/dist/utils/base-repository-find.js:312
    at loadShippingAdjustments (.../base-repository-find.js:306)
    at OrderRepository.findAndCount (.../base-repository-find.js:255)
    at async OrderModuleService.listAndCountOrders
⮑ [export-orders -> export-orders (invoke)]

That stack is the fingerprint. If you see it, you have this bug and no amount of retrying, queue tuning, or worker scaling will help. If you instead see a timeout, an OOM kill, or a Redis connection error, you have a different problem and the rest of this doesn't apply.

The reproduction is narrow enough to be worth stating precisely: it needs at least one non-draft order that has a shipping method. Orders without shipping methods export fine, because the adjustment loader early-returns before it can throw. A development store seeded with bare orders will happily export all day while production fails on every attempt — which is exactly how a regression like this survives a staging pass.

You can reproduce it programmatically, which is the fastest way to confirm without touching the admin:

import { exportOrdersWorkflow } from "@medusajs/medusa/core-flows"
import { defaultAdminExportOrderFields } from "@medusajs/medusa/api/admin/orders/query-config"

await exportOrdersWorkflow(container).run({
  input: { select: [...defaultAdminExportOrderFields], filter: {} },
})

It fails identically in an integration test. That's your regression test once you've patched.

The cause is a field-projection guard that doesn't cover its own caller

2.18.0 introduced manual shipping-adjustment loading in @medusajs/order's find and findAndCount, in dist/utils/base-repository-find.js. When the populate list contains shipping_methods.shipping_method.adjustments, loadShippingAdjustments() runs after the main query and throws unless every order_shipping row has its version column loaded.

There is a guard meant to add that column automatically. It only fires when the caller's field projection already contains an explicit dotted shipping_methods.shipping_method.* entry:

if (config.options.fields?.some((f) => f.includes("shipping_methods.shipping_method."))) {
    config.options.fields.push(isRelatedEntity
        ? "order.shipping_methods.version"
        : "shipping_methods.version");
}

The export workflow's projection is defaultAdminExportOrderFields — totals plus *shipping_methods, with adjustments pulled in via the totals decoration. It contains no dotted shipping_methods.shipping_method. entry. So version never gets selected, the loader runs anyway, and it throws.

The guard keys off the wrong condition. It checks the shape of the projection rather than whether adjustments are actually going to be loaded. Any caller that populates shipping adjustments without explicit dotted shipping-method fields hits this the same way, so if you have custom admin endpoints or scheduled jobs that list orders with totals, check them too — the export is just the most visible victim. In 2.17.2, findAndCount never called loadShippingAdjustments(), which is why exports worked there.

The silence is a second, separate bug

The export-orders workflow has a notifyOnFailureStep that should push "Failed to export orders, please try again later." to the notification feed. It doesn't fire. When the export step fails, neither the success nor the failure notification reaches the operator.

We'd treat that as the more serious of the two issues even though it isn't the one that broke the export. A loud failure is an incident with a ticket attached. A silent failure is a merchant assuming they have data they don't have. If you run finance or fulfilment off these CSVs, the gap between "export failed" and "export appeared to work" is the gap between a bad afternoon and a bad month-end close. Worth checking whether your other Medusa workflows have failure notifications you've never actually seen fire — an untested failure path is a failure path that doesn't work.

Three interim paths, in the order we'd pick them

Upstream hasn't shipped a fix as of 2.19.0. Until it does:

Patch the adjustment loader. This is the option we'd take on a production store. Push the version field whenever shipping adjustments will be loaded and a projection exists — a missing projection already selects everything, so there's nothing to fix in that case. Apply at both the find and findAndCount call sites:

const shippingVersionField = isRelatedEntity
    ? "order.shipping_methods.version"
    : "shipping_methods.version";
if (config.options.fields &&
    !config.options.fields.includes(shippingVersionField)) {
    config.options.fields.push(shippingVersionField);
}

The issue reporter runs exactly this against 2.19.0 via patch-package in production: the export completes and the success feed notification comes back. It's additive — it adds a column to a projection — which is about as low-risk as a vendored patch gets. Pin the version, commit the patch file, and add the programmatic reproduction above as a test so you find out when a future upgrade makes the patch redundant or, worse, silently stops applying.

Pin to 2.17.2. Correct if you haven't upgraded yet and have no reason to. It's the wrong answer if you're already on 2.18/2.19 and depending on anything shipped since — you'd be trading a known bug for a rollback of everything else.

Export via the query layer. If you need a CSV this week and don't want to touch node_modules, build the projection yourself with an explicit dotted shipping_methods.shipping_method. field so the existing guard fires, and write the CSV from a custom endpoint or script. More code to own, but it sidesteps the vendored patch entirely and gives you a column set you control. Reasonable if your finance export was going to diverge from the admin default anyway.

What this says about upgrade discipline on Medusa

Medusa moves fast, and the bug here isn't sloppiness — it's a performance-motivated refactor whose guard clause didn't anticipate one of its own in-tree callers. That's a normal class of regression on a young, actively developed platform, and it's the trade you accept in exchange for the velocity. What it argues against is treating minor version bumps as routine on a store where exports feed accounting.

The practical adjustment is cheap: keep at least one non-draft order with a shipping method in whatever environment you smoke-test against, and run the export as part of your upgrade checklist rather than trusting that the admin's optimistic "you'll be notified" means the job started. This particular regression would have been caught in about ninety seconds by a staging store with realistic data.

If you're weighing a Magento 2 to Medusa move and this looks alarming, calibrate rather than panic. Every platform has this failure mode; Magento's version is a bin/magento setup:upgrade that leaves a schema half-migrated. The difference is that Medusa's source is small enough to read, the root cause here was findable in one file, and the fix is nine lines. That's a real property of the platform and it's worth pricing into the decision alongside the churn.

Do this today

Grep your logs for Shipping method version is required to load adjustments. If it's there, you've been losing exports and nobody told you — check how long, and warn whoever depends on that CSV before you fix anything, because they may have been working from stale data. Then apply the patch, pin the version, and add the workflow reproduction to your test suite so the next upgrade tells you something.

If you're mid-migration and hitting this on a store that isn't live yet, it's a good moment to decide your upgrade cadence deliberately rather than by default. We plan migrations with an explicit pin-and-test policy for exactly this reason — the platform is worth running, and running it well means not taking every minor release on the day it lands. Get in touch if you want a second pair of eyes on an upgrade plan or a stuck export.

Need this done on a real stack?

Magento 2, Adobe Commerce, migrations to Medusa.js or Vendure, enterprise Next.js, WordPress, and AI automation.

Contact us