medusajs/medusa · error · MedusaError

Cannot add promotions to campaign where currency_code don't

Error message

Cannot add promotions to campaign where currency_code don't match.

What it means

Thrown when adding promotions to a campaign whose budget type is SPEND: every promotion's application_method.currency_code must match the campaign budget's currency_code. A mismatch (including a promotion with a null currency_code against a budget with one, or vice versa) rejects the whole batch with INVALID_DATA.

Source

Thrown at packages/modules/promotion/src/services/promotion-module.ts:2045

    if (diff.length > 0) {
      throw new MedusaError(
        MedusaError.Types.NOT_FOUND,
        `Cannot add promotions (${diff.join(
          ","
        )}) to campaign. These promotions are either already part of a campaign or not found.`
      )
    }

    const promotionsWithInvalidCurrency = promotionsToAdd.filter(
      (promotion) =>
        campaign.budget?.type === CampaignBudgetType.SPEND &&
        promotion.application_method?.currency_code !==
          campaign?.budget?.currency_code
    )

    if (promotionsWithInvalidCurrency.length > 0) {
      throw new MedusaError(
        MedusaError.Types.INVALID_DATA,
        `Cannot add promotions to campaign where currency_code don't match.`
      )
    }

    await this.promotionService_.update(
      promotionsToAdd.map((promotion) => ({
        id: promotion.id,
        campaign_id: campaign.id,
      })),
      sharedContext
    )

    return promotionsToAdd.map((promo) => promo.id)
  }

  @InjectManager()
  @EmitEvents()

View on GitHub (pinned to 5e06e544a2)

Solutions

  1. Match the campaign budget currency to the promotions' currency (update campaign budget currency_code or the application methods' currency_code)
  2. Only attach promotions whose application_method.currency_code equals the campaign budget currency; group promotions into per-currency campaigns
  3. If promotions are percentage-based and have no currency, give the application method the campaign's currency or use a usage-type budget instead of spend

Example fix

// before
await campaignModule.addPromotionsToCampaign(usdCampaignId, {
  promo_ids: [eurPromoId],
})

// after
const promos = await promotionModule.listPromotions({ id: promoIds }, { relations: ["application_method"] })
const matching = promos
  .filter((p) => p.application_method?.currency_code === campaignBudgetCurrency)
  .map((p) => p.id)
await campaignModule.addPromotionsToCampaign(usdCampaignId, { promo_ids: matching })
Defensive patterns

Strategy: validation

Validate before calling

const budgetCurrency = campaign.budget?.type === "spend" ? campaign.budget.currency_code : null
const eligible = promotions
  .filter((p) => !budgetCurrency || p.application_method?.currency_code === budgetCurrency)
  .map((p) => p.id)

Type guard

const matchesCampaignCurrency = (promo: PromotionDTO, campaign: CampaignDTO): boolean => campaign.budget?.type !== "spend" || promo.application_method?.currency_code === campaign.budget?.currency_code

Try / catch

try { await addPromotionsToCampaign(...) } catch (e) { if (e.type === MedusaError.Types.INVALID_DATA && /currency_code/.test(e.message)) { /* split promotions by currency into separate campaigns */ } throw e }

Prevention

When it happens

Trigger: Calling addPromotionsToCampaign where campaign.budget.type === 'spend' and one or more promotions have application_method.currency_code different from campaign.budget.currency_code — e.g., a USD fixed promotion added to an EUR-budget campaign, or a percentage promotion (no currency) added to a currency-budgeted campaign.

Common situations: Multi-region stores mixing currencies, creating campaigns before deciding budget currency, promotions created without currency_code (percentage type) being attached to spend-budget campaigns, default currency assumptions in seed scripts.

Related errors


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