medusajs/medusa · error · MedusaError
Promotions with ids (${diff.join(",")}) not found.
Error message
Promotions with ids (${diff.join(",")}) not found. What it means
Thrown by removePromotionsFromCampaign: some promotion ids supplied in promotionsToRemove do not exist in the resolved promotionIds set (deleted, never attached to this campaign, or invalid). The arrayDifference is non-empty so the removal request is rejected with NOT_FOUND.
Source
Thrown at packages/modules/promotion/src/services/promotion-module.ts:2093
data: PromotionTypes.AddPromotionsToCampaignDTO,
@MedusaContext() sharedContext: Context = {}
) {
const { id, promotion_ids: promotionIds = [] } = data
await this.campaignService_.retrieve(id, {}, sharedContext)
const promotionsToRemove = await this.promotionService_.list(
{ id: promotionIds },
{},
sharedContext
)
const diff = arrayDifference(
promotionsToRemove.map((p) => p.id),
promotionIds
)
if (diff.length > 0) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`Promotions with ids (${diff.join(",")}) not found.`
)
}
await this.promotionService_.update(
promotionsToRemove.map((promotion) => ({
id: promotion.id,
campaign_id: null,
})),
sharedContext
)
return promotionsToRemove.map((promo) => promo.id)
}
}
View on GitHub (pinned to 5e06e544a2)
Solutions
- Re-fetch the campaign's promotions (campaign promotion list) and intersect with the ids you intend to remove
- Make removal idempotent client-side: ignore/remove already-absent ids before calling
- Guard against double submits in the UI
Example fix
// before
await campaignModule.removePromotionsFromCampaign(campaignId, {
promo_ids: ["promo_already_removed"],
})
// after
const current = await promotionModule.listPromotions({ campaign_id: campaignId })
const toRemove = requestedIds.filter((id) => current.some((p) => p.id === id))
if (toRemove.length) {
await campaignModule.removePromotionsFromCampaign(campaignId, { promo_ids: toRemove })
} Defensive patterns
Strategy: validation
Validate before calling
const attached = await promotionModule.listPromotions({ campaign_id: campaignId })
const attachedIds = new Set(attached.map((p) => p.id))
const toRemove = requestedIds.filter((id) => attachedIds.has(id)) Type guard
const isAttachedToCampaign = (id: string, attachedIds: Set<string>): boolean => attachedIds.has(id)
Try / catch
try { await removePromotionsFromCampaign(...) } catch (e) { if (e.type === MedusaError.Types.NOT_FOUND) { /* refetch campaign promotions, filter, retry */ } throw e } Prevention
- Make removal idempotent client-side
- Disable the remove button while a request is in flight
- Refresh campaign promotions after mutations
When it happens
Trigger: Calling removePromotionsFromCampaign with ids that are not currently linked to the campaign, were already removed (double-submit), were hard-deleted, or are typos.
Common situations: Double-clicking a remove button firing two requests, UI state desync after another admin removed the promotion, deleted promotions lingering in a client-side list.
Related errors
- Cannot add promotions (${diff.join(",")}) to campaign. These
- Cannot add promotions to campaign where currency_code don't
- Promotion code "${missingPromoCodes[0]}" not found / Promoti
- "items" should be present as an array in the context to comp
- "${contextKey}" should be present as an array in the context
AI-assisted analysis of medusajs/medusa@5e06e544a2 (2026-08-27).
Data as JSON: /api/errors/d52c9d76581cf07b.
Report an issue: GitHub.