medusajs/medusa · error · MedusaError
Invalid rule attribute - ${ruleAttributeId}
Error message
Invalid rule attribute - ${ruleAttributeId} What it means
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.
Source
Thrown at packages/medusa/src/api/admin/promotions/utils/validate-rule-attribute.ts:35
const {
promotionType,
ruleType,
ruleAttributeId,
applicationMethodType,
applicationMethodTargetType,
} = attributes
const ruleAttributes =
getRuleAttributesMap({
promotionType,
applicationMethodType,
applicationMethodTargetType,
})[ruleType] || []
const ruleAttribute = ruleAttributes.find((obj) => obj.id === ruleAttributeId)
if (!ruleAttribute) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Invalid rule attribute - ${ruleAttributeId}`
)
}
}
View on GitHub (pinned to 5e06e544a2)
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
Example fix
// before
GET /admin/promotions/rule-attributes?rule_type=rules&rule_attribute_id=region_id_old
// after
// fetch valid attributes first
const attrs = await getRuleAttributes(ruleType, applicationMethodTargetType)
if (!attrs.some((a) => a.id === ruleAttributeId)) {
throw new Error(`Pick one of: ${attrs.map((a) => a.id).join(", ")}`)
} Defensive patterns
Strategy: validation
Validate before calling
const attrs = await getRuleAttributes(ruleType, applicationMethodTargetType)
const valid = new Set(attrs.map((a) => a.id))
if (!valid.has(ruleAttributeId)) {
throw new Error(`rule_attribute must be one of: ${[...valid].join(", ")}`)
} Type guard
const isValidRuleAttribute = (id: string, attrs: { id: string }[]) =>
attrs.some((a) => a.id === id) Try / catch
try {
await validateRuleAttribute(...)
} catch (e: any) {
if (e.statusCode === 400 && /Invalid rule attribute/.test(e.message)) refreshAttributes()
else throw e
} Prevention
- Populate attribute pickers from the live attributes endpoint
- Send attribute ids verbatim, never labels
- Re-sync attribute lists after Medusa upgrades
When it happens
Trigger: 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.).
Common situations: 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.
Related errors
- Invalid param rule_type (${ruleType})
- promotion's status should be one of - ${allowedStatuses.join
- No product ids passed to remove from price list
- Promotion with id or code: ${idOrCode} was not found
- Rule must have an attribute, an operator and a value
AI-assisted analysis of medusajs/medusa@5e06e544a2 (2026-08-27).
Data as JSON: /api/errors/9e56d3aac3125e74.
Report an issue: GitHub.