{"record":{"id":"5638e7849f675e5c","repo":"antiwork/gumroad","slug":"you-have-to-confirm-your-email-address-before-you","errorCode":null,"errorMessage":"You have to confirm your email address before you can do that.","messagePattern":"You have to confirm your email address before you can do that\\.","errorType":"exception","errorClass":"Installment::PreviewEmailError","httpStatus":422,"severity":"error","filePath":"app/models/installment.rb","lineNumber":407,"sourceCode":"    checkout_url ||= default_checkout_url\n\n    doc = Nokogiri::HTML.fragment(message_with_inline_syntax_highlighting_and_upsells)\n    doc.search(\"#{PRODUCT_LIST_PLACEHOLDER_TAG_NAME}\").each do |node|\n      node.replace(\n        ApplicationController.renderer.render(\n          template: \"posts/abandoned_cart_products_list\",\n          layout: false,\n          assigns: { products: },\n        )\n      )\n    end\n    doc.search(\"a[href='#{default_checkout_url}']\").each { _1[\"href\"] = checkout_url } if default_checkout_url != checkout_url\n    doc.to_html\n  end\n\n  def send_preview_email(recipient_user)\n    if recipient_user.has_unconfirmed_email?\n      raise PreviewEmailError, \"You have to confirm your email address before you can do that.\"\n    elsif abandoned_cart_type?\n      CustomerMailer.abandoned_cart_preview(recipient_user.id, id).deliver_later\n    else\n      recipient = { email: recipient_user.email }\n      recipient[:url_redirect] = UrlRedirect.find_or_create_by!(installment: self, purchase: nil) if has_files?\n      begin\n        PostEmailApi.process(post: self, recipients: [recipient], preview: true)\n      rescue ResendApiResponseError, SendGridApiResponseError\n        # Either provider can be the one that actually sends this preview: the\n        # recipient's domain decides (see\n        # MailerInfo::UNITED_INTERNET_RECIPIENT_DOMAINS), as does the random\n        # split, so a failure from either has to become the same friendly error\n        # rather than an unhandled 500 on the preview request.\n        raise PreviewEmailError, \"Failed to send preview email. Please try again later.\"\n      end\n    end\n  end\n","sourceCodeStart":389,"sourceCodeEnd":425,"githubUrl":"https://github.com/antiwork/gumroad/blob/afeacbd394069a1cbf0c6c50ee8e900925050370/app/models/installment.rb#L389-L425","documentation":"Raised as Installment::PreviewEmailError (defined at app/models/installment.rb:1088) by Installment#send_preview_email when the recipient user has_unconfirmed_email? — i.e. unconfirmed_email is present or the user is not confirmed (User#has_unconfirmed_email?, app/models/user.rb:1132). Previews go to the seller's own address, so an unconfirmed address means the preview cannot be delivered. Controllers rescue this error and render its message as 422 (see app/controllers/api/internal/installments/preview_emails_controller.rb:14).","triggerScenarios":"POST to the installment preview-email endpoints (Api::Internal::Installments::PreviewEmailsController#create or Api::V2::EmailsController preview) with the acting user — current_seller, or the impersonating user when impersonation is active — unconfirmed or holding a pending unconfirmed_email (address-change flow).","commonSituations":"New seller never clicked the confirmation link and immediately tries to preview a post/workflow email; seller just changed their email address (unconfirmed_email set) and previews before confirming; admin impersonating an unconfirmed seller.","solutions":["Confirm the email address first: resend the confirmation email from account settings and click the link, then retry the preview.","If the address was recently changed, confirm the new address to clear unconfirmed_email.","Gate the 'Send preview' button on user.confirmed? && user.unconfirmed_email.blank? client-side.","Handle Installment::PreviewEmailError as 422 with its message — it is a user-actionable error, not a 500."],"exampleFix":"# before\ninstallment.send_preview_email(current_seller) # PreviewEmailError\n\n# after\nif current_seller.has_unconfirmed_email?\n  return { error: \"Confirm your email address before sending a preview.\" }\nend\ninstallment.send_preview_email(current_seller)","handlingStrategy":"validation","validationCode":"return { error: \"Confirm your email first.\" } if recipient_user.has_unconfirmed_email?","typeGuard":null,"tryCatchPattern":"begin\n  installment.send_preview_email(user)\nrescue Installment::PreviewEmailError => e\n  render json: { message: e.message }, status: :unprocessable_entity\nend","preventionTips":["Gate preview buttons on user.confirmed? && user.unconfirmed_email.blank?.","Remember a pending email change (unconfirmed_email set) counts as unconfirmed.","When impersonating, the impersonated user's confirmation state is what matters."],"tags":["installments","emails","preview","email-confirmation","rails"],"backgroundTag":"email-not-confirmed","analyzedSha":"afeacbd394069a1cbf0c6c50ee8e900925050370","analyzedAt":"2026-08-21T17:58:52.159Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}