Fixing a 404 in Medusa's Admin

A shipping profile row linked to a route that was never registered. The fix is one line; the work was proving which of four callers disagreed with the router.

Fixing a 404 in Medusa's Admin

Medusa's admin has a Shipping Profiles list. You click a row, expecting the profile. You get the SPA's "no page at this address" instead.

The profile exists. The API returns it. The row just points at a door that was never built.

What was wrong

One line, in the table adapter that builds the row links:

getRowHref: (row) => `/settings/shipping-profiles/${row.id}`,

That path is not a registered route.

Proving it rather than assuming it

The obvious guess is "the route is missing." It isn't — it's registered somewhere else. The way to be sure is to read the route table rather than the code that links into it.

In get-route.map.tsx, the string path: "shipping-profiles" appears exactly once, and it is nested:

settings
└── locations                        (line 1477)
    └── shipping-profiles            (line 1493)
        ├── ""                       → list
        └── :shipping_profile_id     (line 1516) → detail

So the only detail route that exists is /settings/locations/shipping-profiles/:id. There is no bare /settings/shipping-profiles to match, which is why React Router falls through to the catch-all and the SPA renders its 404.

"Appears exactly once" is the part that turns a guess into a fact. If that string had appeared twice, the fix would have been a different conversation.

The rest of the codebase already knew

Every other place that navigates to a shipping profile uses the full path. The adapter was the only outlier:

CallerPath
Product shipping-profile section/settings/locations/shipping-profiles/${id} ✅
Create form redirect/settings/locations/shipping-profiles/${id} ✅
Delete redirect/settings/locations/shipping-profiles ✅
List table adapter/settings/shipping-profiles/${id} ❌

Three against one. That is about as unambiguous as a bug gets — I didn't have to decide what the correct path should be, only which of the four disagreed with the router.

The fix

-    getRowHref: (row) => `/settings/shipping-profiles/${row.id}`,
+    getRowHref: (row) => `/settings/locations/shipping-profiles/${row.id}`,

The part that would have got it rejected

Medusa uses changesets. Before opening anything I checked the last eight merged fix(dashboard) PRs:

#16455 fix(dashboard): reload on preload error        | changeset: YES
#16495 fix(dashboard): fix create fulfillment form    | changeset: YES
#16513 fix(dashboard): remove duplicate Medusa Cloud  | changeset: YES
#16450 fix(dashboard): set the lang attribute         | changeset: YES
... 8 of 8

Eight out of eight. A one-line fix with no changeset would have bounced on the first review, and the reviewer's time would have gone on process instead of the bug.

This is the cheapest possible check and I nearly skipped it. Reading merged PRs tells you what a project actually requires, which is not always what CONTRIBUTING.md says — and here it wasn't written down at all.

The PR is medusajs/medusa#16628.

What I'd take from it

When a link 404s, don't start by asking what the link should say. Find the routing table and count how many times the path segment appears. The router is the only thing that decides what exists; everything else is a claim about it.