{"record":{"id":"5555727d309938fc","repo":"medusajs/medusa","slug":"no-product-ids-passed-to-remove-from-price-list","errorCode":null,"errorMessage":"No product ids passed to remove from price list","messagePattern":"No product ids passed to remove from price list","errorType":"http","errorClass":"MedusaError","httpStatus":400,"severity":"error","filePath":"packages/medusa/src/api/admin/price-lists/[id]/products/route.ts","lineNumber":21,"sourceCode":"import { MedusaError } from \"@medusajs/framework/utils\"\nimport {\n  AuthenticatedMedusaRequest,\n  MedusaResponse,\n} from \"@medusajs/framework/http\"\nimport { fetchPriceList, fetchPriceListPriceIdsForProduct } from \"../../helpers\"\n\nexport const POST = async (\n  req: AuthenticatedMedusaRequest<\n    HttpTypes.AdminLinkPriceListProducts,\n    HttpTypes.AdminPriceListParams\n  >,\n  res: MedusaResponse<HttpTypes.AdminPriceListResponse>\n) => {\n  const id = req.params.id\n  const { remove = [] } = req.validatedBody\n\n  if (!remove.length) {\n    throw new MedusaError(\n      MedusaError.Types.INVALID_DATA,\n      \"No product ids passed to remove from price list\"\n    )\n  }\n\n  const productPriceIds = await fetchPriceListPriceIdsForProduct(\n    id,\n    remove,\n    req.scope\n  )\n\n  const workflow = batchPriceListPricesWorkflow(req.scope)\n  await workflow.run({\n    input: {\n      data: {\n        id,\n        create: [],\n        update: [],","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/medusajs/medusa/blob/5e06e544a296b9033f20f71f11c559f81a0e5739/packages/medusa/src/api/admin/price-lists/[id]/products/route.ts#L3-L39","documentation":"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.","triggerScenarios":"Calling the endpoint with { remove: [] } or omitting remove entirely (it defaults to []), or sending ids under the wrong key (e.g. product_ids).","commonSituations":"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.","solutions":["Only call the endpoint when at least one product id is selected; guard client-side on remove.length > 0","Send the body as { remove: [\"prod_...\"] } with the remove key","If you meant to assign products to a price list, use the appropriate product-add endpoint instead"],"exampleFix":"// before\nawait sdk.client.fetch(`/admin/price-lists/${id}/products`, { method: \"POST\", body: { remove: selectedIds } }) // selectedIds may be []\n\n// after\nif (selectedIds.length > 0) {\n  await sdk.client.fetch(`/admin/price-lists/${id}/products`, { method: \"POST\", body: { remove: selectedIds } })\n}","handlingStrategy":"validation","validationCode":"const toRemove = selectedIds.filter(Boolean)\nif (toRemove.length === 0) return // nothing to do, skip the call\nawait sdk.client.fetch(`/admin/price-lists/${id}/products`, { method: \"POST\", body: { remove: toRemove } })","typeGuard":"const hasRemovals = (b: { remove?: string[] }) => Array.isArray(b.remove) && b.remove.length > 0","tryCatchPattern":null,"preventionTips":["Disable submit when no rows are selected","Validate request bodies with zod: z.object({ remove: z.string().array().min(1) })","Check payload keys before sending"],"tags":["admin","price-lists","validation","rest-api"],"backgroundTag":"invalid-request-body","analyzedSha":"5e06e544a296b9033f20f71f11c559f81a0e5739","analyzedAt":"2026-08-27T07:24:39.599Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}