medusajs/medusa · error · MedusaError

No product ids passed to remove from price list

Error message

No product ids passed to remove from price list

What it means

Thrown by POST /admin/price-lists/:id/products when the body's remove array is empty. The endpoint exists specifically to remove products from a price list, so a request without at least one product id in remove is rejected as INVALID_DATA before any workflow runs.

Source

Thrown at packages/medusa/src/api/admin/price-lists/[id]/products/route.ts:21

import { MedusaError } from "@medusajs/framework/utils"
import {
  AuthenticatedMedusaRequest,
  MedusaResponse,
} from "@medusajs/framework/http"
import { fetchPriceList, fetchPriceListPriceIdsForProduct } from "../../helpers"

export const POST = async (
  req: AuthenticatedMedusaRequest<
    HttpTypes.AdminLinkPriceListProducts,
    HttpTypes.AdminPriceListParams
  >,
  res: MedusaResponse<HttpTypes.AdminPriceListResponse>
) => {
  const id = req.params.id
  const { remove = [] } = req.validatedBody

  if (!remove.length) {
    throw new MedusaError(
      MedusaError.Types.INVALID_DATA,
      "No product ids passed to remove from price list"
    )
  }

  const productPriceIds = await fetchPriceListPriceIdsForProduct(
    id,
    remove,
    req.scope
  )

  const workflow = batchPriceListPricesWorkflow(req.scope)
  await workflow.run({
    input: {
      data: {
        id,
        create: [],
        update: [],

View on GitHub (pinned to 5e06e544a2)

Solutions

  1. Only call the endpoint when at least one product id is selected; guard client-side on remove.length > 0
  2. Send the body as { remove: ["prod_..."] } with the remove key
  3. If you meant to assign products to a price list, use the appropriate product-add endpoint instead

Example fix

// before
await sdk.client.fetch(`/admin/price-lists/${id}/products`, { method: "POST", body: { remove: selectedIds } }) // selectedIds may be []

// after
if (selectedIds.length > 0) {
  await sdk.client.fetch(`/admin/price-lists/${id}/products`, { method: "POST", body: { remove: selectedIds } })
}
Defensive patterns

Strategy: validation

Validate before calling

const toRemove = selectedIds.filter(Boolean)
if (toRemove.length === 0) return // nothing to do, skip the call
await sdk.client.fetch(`/admin/price-lists/${id}/products`, { method: "POST", body: { remove: toRemove } })

Type guard

const hasRemovals = (b: { remove?: string[] }) => Array.isArray(b.remove) && b.remove.length > 0

Prevention

When it happens

Trigger: Calling the endpoint with { remove: [] } or omitting remove entirely (it defaults to []), or sending ids under the wrong key (e.g. product_ids).

Common situations: Frontends that always call the endpoint even when the user deselected nothing, API consumers confusing this route with the products batch route that also accepts add/list fields, or a serialization bug producing an empty array.

Related errors


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