{"record":{"id":"9f475dbb71d05d56","repo":"medusajs/medusa","slug":"product-not-found","errorCode":null,"errorMessage":"Product not found","messagePattern":"Product not found","errorType":"exception","errorClass":"MedusaError","httpStatus":404,"severity":"error","filePath":"packages/medusa/src/api/admin/products/[id]/route.ts","lineNumber":27,"sourceCode":"import { remapKeysForProduct, remapProductResponse } from \"../helpers\"\nimport { MedusaError } from \"@medusajs/framework/utils\"\nimport { AdditionalData, HttpTypes } from \"@medusajs/framework/types\"\nimport { refetchEntity } from \"@medusajs/framework/http\"\n\nexport const GET = async (\n  req: AuthenticatedMedusaRequest<HttpTypes.AdminGetProductParams>,\n  res: MedusaResponse<HttpTypes.AdminProductResponse>\n) => {\n  const selectFields = remapKeysForProduct(req.queryConfig.fields ?? [])\n  const product = await refetchEntity({\n    entity: \"product\",\n    idOrFilter: req.params.id,\n    scope: req.scope,\n    fields: selectFields,\n  })\n\n  if (!product) {\n    throw new MedusaError(MedusaError.Types.NOT_FOUND, \"Product not found\")\n  }\n\n  res.status(200).json({ product: remapProductResponse(product) })\n}\n\nexport const POST = async (\n  req: AuthenticatedMedusaRequest<\n    HttpTypes.AdminUpdateProduct & AdditionalData,\n    HttpTypes.SelectParams\n  >,\n  res: MedusaResponse<HttpTypes.AdminProductResponse>\n) => {\n  const { additional_data, ...update } = req.validatedBody\n\n  const existingProduct = await refetchEntity({\n    entity: \"product\",\n    idOrFilter: req.params.id,\n    scope: req.scope,","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/medusajs/medusa/blob/5e06e544a296b9033f20f71f11c559f81a0e5739/packages/medusa/src/api/admin/products/[id]/route.ts#L9-L45","documentation":"Thrown by GET /admin/products/:id when refetchProduct returns no record. The product is looked up by idOrFilter with the route's selected fields; an empty result (deleted product, wrong id) triggers NOT_FOUND.","triggerScenarios":"Calling GET /admin/products/prod_123 for a product that was deleted, never existed, or belongs to a different store/environment.","commonSituations":"Deep-linked admin pages to deleted products, webhooks retrying for products removed in the meantime, or stale ids after database re-seeding.","solutions":["Verify the product via GET /admin/products?q=<title> before deep-linking or processing","Handle 404 in consumers (webhooks, importers) by skipping or marking the record deleted","Confirm environment consistency for ids coming from external systems"],"exampleFix":"// before\nconst { product } = await sdk.admin.product.retrieve(prodId)\n\n// after\ntry {\n  const { product } = await sdk.admin.product.retrieve(prodId)\n} catch (e) {\n  if (e.statusCode === 404) { /* mark record as deleted upstream */ return }\n  throw e\n}","handlingStrategy":"try-catch","validationCode":"const { products } = await sdk.admin.product.list({ id: [prodId], fields: \"id\" })\nif (products.length === 0) throw new Error(`Product ${prodId} not found`)","typeGuard":"const isProduct = (v: unknown): v is HttpTypes.AdminProduct =>\n  !!v && typeof v === \"object\" && \"id\" in v && \"title\" in v","tryCatchPattern":"try {\n  return await getProduct(prodId)\n} catch (e: any) {\n  if (e.statusCode === 404) return null // deleted upstream\n  throw e\n}","preventionTips":["Webhook consumers must treat product 404 as deletion signal","Don't cache product ids across re-seeds","Guard deep links with an existence check"],"tags":["admin","products","not-found","rest-api"],"backgroundTag":"resource-not-found","analyzedSha":"5e06e544a296b9033f20f71f11c559f81a0e5739","analyzedAt":"2026-08-27T07:24:39.599Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}