medusajs/medusa · error · MedusaError

Could not find all existing links from data

Error message

Could not find all existing links from data

What it means

Thrown by updateRemoteLinksStep when the number of existing link rows fetched before update does not match the number of entries in the input data. The step validates that every link passed in already exists before upserting; a mismatch means at least one link could not be found.

Source

Thrown at packages/core/core-flows/src/common/steps/update-remote-links.ts:50

 */
export const updateRemoteLinksStep = createStep(
  updateRemoteLinksStepId,
  async (data: LinkDefinition[], { container }) => {
    if (!data?.length) {
      return new StepResponse([], [])
    }

    const link = container.resolve<Link>(ContainerRegistrationKeys.LINK)

    // Fetch all existing links and throw an error if any weren't found
    const dataBeforeUpdate = (await link.list(data, {
      asLinkDefinition: true,
    })) as LinkDefinition[]

    const unequal = dataBeforeUpdate.length !== data.length

    if (unequal) {
      throw new MedusaError(
        MedusaError.Types.NOT_FOUND,
        `Could not find all existing links from data`
      )
    }

    // link.create here performs an upsert. By performing validation above, we can ensure
    // that this method will always perform an update in these cases
    await link.create(data)

    return new StepResponse(data, dataBeforeUpdate)
  },
  async (dataBeforeUpdate, { container }) => {
    if (!dataBeforeUpdate?.length) {
      return
    }

    const link = container.resolve<Link>(ContainerRegistrationKeys.LINK)

View on GitHub (pinned to 5e06e544a2)

Solutions

  1. Inspect each entry in data and verify both sides (e.g. cart_id and promotion_id) still exist before calling the step
  2. Re-query the links via link.list() and pass the fresh link records instead of hand-built definitions
  3. Ensure you are creating links (createRemoteLinkStep) first if they may not exist yet

Example fix

// before
await updateRemoteLinksStep(container).run({
  input: [ { [Modules.CART]: { cart_id }, [Modules.PROMOTION]: { promotion_id } , data: {...} } ],
})

// after: only update links that actually exist
const existing = await link.list(MODULES_LINK, { cart_id })
const toUpdate = existing.filter((l) => promoIds.includes(l.promotion_id))
await updateRemoteLinksStep(container).run({ input: toUpdate })
Defensive patterns

Strategy: validation

Validate before calling

const existing = await link.list(cartPromotionLink, { cart_id })
const valid = input.filter((d) => existing.some((l) => l.promotion_id === d[Modules.PROMOTION].promotion_id))
if (valid.length !== input.length) throw new Error("Some links no longer exist")

Type guard

const isLinkDefinition = (d: unknown): d is LinkDefinition => !!d && typeof d === "object"

Try / catch

try { await updateRemoteLinksStep(scope).run({ input }) } catch (e) { if (e.type === MedusaError.Types.NOT_FOUND) { /* refetch links and retry with fresh data */ } throw e }

Prevention

When it happens

Trigger: Calling updateRemoteLinksStep (directly or via a workflow that refreshes links, e.g. cart/payment/order promotion links) where one of the link definitions references a nonexistent or already-deleted counterpart, so the pre-update query returns fewer rows than data.length.

Common situations: Passing stale link data referencing deleted records; partial deletion of one side of a link (manual DB cleanup); race where a link was removed concurrently; wrong field names in the LinkDefinition so the lookup misses.

Related errors


AI-assisted analysis of medusajs/medusa@5e06e544a2 (2026-08-27). Data as JSON: /api/errors/70750551d858cbc5. Report an issue: GitHub.