{"record":{"id":"25812f78e6b06fcd","repo":"antiwork/gumroad","slug":"invalid-offer-code","errorCode":null,"errorMessage":"Invalid offer code","messagePattern":"Invalid offer code","errorType":"exception","errorClass":"Link::LinkInvalid","httpStatus":null,"severity":"error","filePath":"app/controllers/bundles/product_controller.rb","lineNumber":160,"sourceCode":"\n      default_offer_code_id = product_permitted_params[:default_offer_code_id]\n\n      return @bundle.default_offer_code = nil if default_offer_code_id.blank?\n\n      offer_code = @bundle.user.offer_codes.alive.find_by_external_id!(default_offer_code_id)\n\n      # The form re-sends the current id on every save, so a currency change\n      # would otherwise be rejected outright by applicable? below. Leave an\n      # unchanged id to Link#clear_detached_default_offer_code, which drops it\n      # when it stops applying.\n      return if offer_code == @bundle.default_offer_code\n\n      raise Link::LinkInvalid, \"Offer code cannot be expired\" if offer_code.inactive?\n      raise Link::LinkInvalid, \"Offer code must apply to this product\" unless offer_code.applicable?(@bundle)\n\n      @bundle.default_offer_code = offer_code\n    rescue ActiveRecord::RecordNotFound\n      raise Link::LinkInvalid, \"Invalid offer code\"\n    end\nend\n","sourceCodeStart":142,"sourceCodeEnd":163,"githubUrl":"https://github.com/antiwork/gumroad/blob/afeacbd394069a1cbf0c6c50ee8e900925050370/app/controllers/bundles/product_controller.rb#L142-L163","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\npatch bundle_product_path(@bundle), params: { product: { default_offer_code_id: stale_external_id } }\n\n# after — resolve a live code owned by the same seller first\ncode = @bundle.user.offer_codes.alive.find_by(external_id: submitted_id)\nraise Link::LinkInvalid, \"Invalid offer code\" unless code\nparams[:product][:default_offer_code_id] = code.external_id","handlingStrategy":"validation","validationCode":"code = @bundle.user.offer_codes.alive.find_by(external_id: submitted_id)\nraise Link::LinkInvalid, \"Invalid offer code\" if code.nil?","typeGuard":"def resolvable_offer_code?(user, external_id)\n  user.offer_codes.alive.exists?(external_id: external_id)\nend","tryCatchPattern":"begin\n  offer_code = user.offer_codes.alive.find_by_external_id!(id)\nrescue ActiveRecord::RecordNotFound\n  # show a picker refreshed with alive codes; do not retry the same id\nend","preventionTips":["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."],"tags":["offer-codes","bundles","record-not-found","link-invalid","validation"],"backgroundTag":"offer-code-not-found","analyzedSha":"afeacbd394069a1cbf0c6c50ee8e900925050370","analyzedAt":"2026-08-21T17:58:52.159Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}