medusajs/medusa · error · MedusaError

Product option value with id "${valueId}" was not found for

Error message

Product option value with id "${valueId}" was not found for option with id "${optionId}"

What it means

Thrown by retrieveProductOptionValue when the query for a product option value filtered by both valueId and optionId returns nothing. It enforces that the value actually belongs to the given option, so a mismatch reads the same as a missing record.

Source

Thrown at packages/medusa/src/api/admin/product-options/[id]/values/[value_id]/route.ts:32

const retrieveProductOptionValue = async (
  req: AuthenticatedMedusaRequest<unknown, unknown>,
  fields?: string[]
): Promise<HttpTypes.AdminProductOptionValue> => {
  // Ensure the value belongs to the option
  const { value_id: valueId, id: optionId } = req.params
  const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)

  const {
    data: [product_option_value],
  } = await query.graph({
    entity: "product_option_value",
    filters: { id: valueId, option_id: optionId },
    fields: fields ?? ["id"],
  })

  if (!product_option_value) {
    throw new MedusaError(
      MedusaError.Types.NOT_FOUND,
      `Product option value with id "${valueId}" was not found for option with id "${optionId}"`
    )
  }

  return product_option_value
}

/**
 * @since 2.16.0
 */
export const GET = async (
  req: AuthenticatedMedusaRequest<{}, HttpTypes.SelectParams>,
  res: MedusaResponse<HttpTypes.AdminProductOptionValueResponse>
) => {
  const product_option_value = await retrieveProductOptionValue(
    req,
    req.queryConfig.fields

View on GitHub (pinned to 5e06e544a2)

Solutions

  1. List the option's values (GET /admin/product-options/:id/values) and confirm the pairing before mutating
  2. Rebuild the value/option id mapping in your client state from a fresh fetch
  3. On 404, re-fetch the option and reselect the correct value id

Example fix

// before
await sdk.client.fetch(`/admin/product-options/${optionId}/values/${valueId}`, { method: "DELETE" })

// after
const { values } = await fetchOptionValues(optionId)
if (!values.some((v) => v.id === valueId)) {
  throw new Error(`Value ${valueId} is not on option ${optionId}`)
}
await sdk.client.fetch(`/admin/product-options/${optionId}/values/${valueId}`, { method: "DELETE" })
Defensive patterns

Strategy: validation

Validate before calling

const { values } = await getOptionValues(optionId)
if (!values.some((v) => v.id === valueId)) throw new Error(`Value ${valueId} not on option ${optionId}`)

Type guard

const belongsToOption = (v: { id: string; option_id?: string }, optionId: string) =>
  v.option_id === optionId || !!v.option_id === false && valuesOf(optionId).includes(v.id)

Try / catch

try {
  await deleteOptionValue(optionId, valueId)
} catch (e: any) {
  if (e.statusCode === 404) { await refetchOption(optionId); return }
  throw e
}

Prevention

When it happens

Trigger: Calling GET/POST/DELETE on /admin/product-options/:id/values/:value_id where the value id exists but belongs to a different option, or either id is wrong/deleted.

Common situations: Frontends keeping option and value ids in separate state and pairing them incorrectly after reordering, copying value ids between options during data imports, or stale ids after option regeneration.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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