antiwork/gumroad · error · Link::LinkInvalid
Invalid offer code
Error message
Invalid offer code
What it means
Raised as `Link::LinkInvalid` from the `rescue ActiveRecord::RecordNotFound` at bundles/product_controller.rb:159-161. `@bundle.user.offer_codes.alive.find_by_external_id!` found no live offer code with that external id owned by the bundle's user — the code was deleted (filtered by the `alive` scope), never existed, or belongs to a different account. `find_by_external_id!` is the bang variant, so a miss raises RecordNotFound which is converted to this friendlier validation message.
Source
Thrown at app/controllers/bundles/product_controller.rb:160
default_offer_code_id = product_permitted_params[:default_offer_code_id]
return @bundle.default_offer_code = nil if default_offer_code_id.blank?
offer_code = @bundle.user.offer_codes.alive.find_by_external_id!(default_offer_code_id)
# The form re-sends the current id on every save, so a currency change
# would otherwise be rejected outright by applicable? below. Leave an
# unchanged id to Link#clear_detached_default_offer_code, which drops it
# when it stops applying.
return if offer_code == @bundle.default_offer_code
raise Link::LinkInvalid, "Offer code cannot be expired" if offer_code.inactive?
raise Link::LinkInvalid, "Offer code must apply to this product" unless offer_code.applicable?(@bundle)
@bundle.default_offer_code = offer_code
rescue ActiveRecord::RecordNotFound
raise Link::LinkInvalid, "Invalid offer code"
end
end
View on GitHub (pinned to afeacbd394)
Solutions
- Reload the product form / offer-code dropdown so it lists only currently alive codes, then re-select a valid default offer code.
- Confirm the id is the code's external id from the same seller account and the same environment you are saving in.
- If the code was deleted by mistake, recreate it and use the new id.
- Leave `default_offer_code_id` blank to clear the assignment if no default code is wanted.
Example fix
# before
patch bundle_product_path(@bundle), params: { product: { default_offer_code_id: stale_external_id } }
# after — resolve a live code owned by the same seller first
code = @bundle.user.offer_codes.alive.find_by(external_id: submitted_id)
raise Link::LinkInvalid, "Invalid offer code" unless code
params[:product][:default_offer_code_id] = code.external_id Defensive patterns
Strategy: validation
Validate before calling
code = @bundle.user.offer_codes.alive.find_by(external_id: submitted_id) raise Link::LinkInvalid, "Invalid offer code" if code.nil?
Type guard
def resolvable_offer_code?(user, external_id) user.offer_codes.alive.exists?(external_id: external_id) end
Try / catch
begin offer_code = user.offer_codes.alive.find_by_external_id!(id) rescue ActiveRecord::RecordNotFound # show a picker refreshed with alive codes; do not retry the same id end
Prevention
- Never cache offer-code dropdowns across sessions; rebuild on form load.
- Scope lookups to the current seller (user.offer_codes) so foreign ids fail fast.
- Keep environment-specific ids out of shared fixtures and scripts.
- After deleting a code, clear it as any product's default in the same change.
When it happens
Trigger: Submitting `default_offer_code_id` for an offer code the seller deleted in another tab; an id copied from a different seller account or a different environment (test id in production); an id typo/truncation in an API payload; the code still exists but is outside the `alive` scope.
Common situations: Two browser tabs open on Offer Codes and the product editor — delete in one, save in the other; front-end caching an old dropdown list of codes; scripts pasting ids between environments.
Related errors
- Offer code cannot be expired
- Offer code must apply to this product
- Invalid offer code
- Offer code cannot be expired
- Offer code must apply to this product
AI-assisted analysis of antiwork/gumroad@afeacbd394 (2026-08-21).
Data as JSON: /api/errors/25812f78e6b06fcd.
Report an issue: GitHub.