{"record":{"id":"9e56d3aac3125e74","repo":"medusajs/medusa","slug":"invalid-rule-attribute-ruleattributeid","errorCode":null,"errorMessage":"Invalid rule attribute - ${ruleAttributeId}","messagePattern":"Invalid rule attribute - (.+?)","errorType":"exception","errorClass":"MedusaError","httpStatus":400,"severity":"error","filePath":"packages/medusa/src/api/admin/promotions/utils/validate-rule-attribute.ts","lineNumber":35,"sourceCode":"  const {\n    promotionType,\n    ruleType,\n    ruleAttributeId,\n    applicationMethodType,\n    applicationMethodTargetType,\n  } = attributes\n\n  const ruleAttributes =\n    getRuleAttributesMap({\n      promotionType,\n      applicationMethodType,\n      applicationMethodTargetType,\n    })[ruleType] || []\n\n  const ruleAttribute = ruleAttributes.find((obj) => obj.id === ruleAttributeId)\n\n  if (!ruleAttribute) {\n    throw new MedusaError(\n      MedusaError.Types.INVALID_DATA,\n      `Invalid rule attribute - ${ruleAttributeId}`\n    )\n  }\n}\n","sourceCodeStart":17,"sourceCodeEnd":41,"githubUrl":"https://github.com/medusajs/medusa/blob/5e06e544a296b9033f20f71f11c559f81a0e5739/packages/medusa/src/api/admin/promotions/utils/validate-rule-attribute.ts#L17-L41","documentation":"Thrown by validateRuleAttribute when the supplied rule attribute id is not in the list of valid attributes for the given rule type. The valid set is derived from the promotion rules context (e.g. rule attributes for a certain application method target type), and the requested attribute must be one of them.","triggerScenarios":"Calling the promotion rule validation endpoints (GET rule attributes flows) with a rule_attribute id that does not exist for the resolved rule_type, or a rule attribute belonging to a different target type (items/shipping_methods/etc.).","commonSituations":"Custom admin UIs offering stale attribute lists after a Medusa upgrade changed available attributes, passing attribute labels instead of ids, or hardcoding attribute ids that no longer exist.","solutions":["Fetch the current valid attributes for the rule type first and restrict the UI/options to that list","Send the attribute's id exactly as returned by the attributes listing endpoint","After upgrading Medusa, re-sync any cached rule attribute lists"],"exampleFix":"// before\nGET /admin/promotions/rule-attributes?rule_type=rules&rule_attribute_id=region_id_old\n\n// after\n// fetch valid attributes first\nconst attrs = await getRuleAttributes(ruleType, applicationMethodTargetType)\nif (!attrs.some((a) => a.id === ruleAttributeId)) {\n  throw new Error(`Pick one of: ${attrs.map((a) => a.id).join(\", \")}`)\n}","handlingStrategy":"validation","validationCode":"const attrs = await getRuleAttributes(ruleType, applicationMethodTargetType)\nconst valid = new Set(attrs.map((a) => a.id))\nif (!valid.has(ruleAttributeId)) {\n  throw new Error(`rule_attribute must be one of: ${[...valid].join(\", \")}`)\n}","typeGuard":"const isValidRuleAttribute = (id: string, attrs: { id: string }[]) =>\n  attrs.some((a) => a.id === id)","tryCatchPattern":"try {\n  await validateRuleAttribute(...)\n} catch (e: any) {\n  if (e.statusCode === 400 && /Invalid rule attribute/.test(e.message)) refreshAttributes()\n  else throw e\n}","preventionTips":["Populate attribute pickers from the live attributes endpoint","Send attribute ids verbatim, never labels","Re-sync attribute lists after Medusa upgrades"],"tags":["admin","promotions","validation","rules"],"backgroundTag":"invalid-request-parameter","analyzedSha":"5e06e544a296b9033f20f71f11c559f81a0e5739","analyzedAt":"2026-08-27T07:24:39.599Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}