{"record":{"id":"74df7bf8c9fd99ef","repo":"postalserver/postal","slug":"invalid-email-address","errorCode":null,"errorMessage":"Invalid email address","messagePattern":"Invalid email address","errorType":"exception","errorClass":"Postal::Error","httpStatus":500,"severity":"error","filePath":"app/controllers/domains_controller.rb","lineNumber":83,"sourceCode":"      else\n        respond_to do |wants|\n          wants.html { flash.now[:alert] = \"We couldn't verify your domain. Please double check you've added the TXT record correctly.\" }\n          wants.json { render json: { flash: { alert: \"We couldn't verify your domain. Please double check you've added the TXT record correctly.\" } } }\n        end\n      end\n    when \"Email\"\n      if params[:code]\n        if @domain.verification_token == params[:code].to_s.strip\n          @domain.mark_as_verified\n          redirect_to_with_json [:setup, organization, @server, @domain], notice: \"#{@domain.name} has been verified successfully. You now need to configure your DNS records.\"\n        else\n          respond_to do |wants|\n            wants.html { flash.now[:alert] = \"Invalid verification code. Please check and try again.\" }\n            wants.json { render json: { flash: { alert: \"Invalid verification code. Please check and try again.\" } } }\n          end\n        end\n      elsif params[:email_address].present?\n        raise Postal::Error, \"Invalid email address\" unless @domain.verification_email_addresses.include?(params[:email_address])\n\n        AppMailer.verify_domain(@domain, params[:email_address], current_user).deliver\n        if @domain.owner.is_a?(Server)\n          redirect_to_with_json verify_organization_server_domain_path(organization, @server, @domain, email_address: params[:email_address])\n        else\n          redirect_to_with_json verify_organization_domain_path(organization, @domain, email_address: params[:email_address])\n        end\n      end\n    end\n  end\n\n  def setup\n    return if @domain.verified?\n\n    redirect_to [:verify, organization, @server, @domain], alert: \"You can't set up DNS for this domain until it has been verified.\"\n  end\n\n  def check","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/postalserver/postal/blob/d038eaa8c763d3cafa797ccd6f773d53470bd336/app/controllers/domains_controller.rb#L65-L101","documentation":"Raised by Postal's DomainsController#verify action (email verification branch). Postal can verify domain ownership by emailing a code to one of a fixed set of administrative mailboxes generated per domain (@domain.verification_email_addresses, e.g. postmaster@/abuse@host). If the submitted email_address param is not in that list, Postal raises Postal::Error instead of sending the verification mail, because the recipient would not prove control of the domain.","triggerScenarios":"POST to the domain verify action with mode=Email and params[:email_address] set to any mailbox not in @domain.verification_email_addresses - e.g. typing a custom address into the form, tampering with the form/API payload, or resubmitting a stale form after the domain name was changed (old generated addresses no longer match).","commonSituations":"Editing the email input in the verify-domain UI before submitting; scripts/API calls guessing an address; case or whitespace differences between the submitted param and the generated list; a saved form from before the domain record was renamed.","solutions":["Submit one of the exact addresses exposed by the domain's verification_email_addresses (the values the UI dropdown offers)","Strip and normalize params[:email_address] (e.g. .to_s.strip.downcase) before comparing, if the mismatch is only whitespace/case","If the domain was renamed, reload the verify page so the form uses the newly generated addresses","As a maintainer, replace the raise with a respond_to flash/alert (mirroring the invalid-code branch above it) so bad input returns a 4xx instead of a 500","Verify the domain via the DNS/records method instead of the email method"],"exampleFix":"# before\nraise Postal::Error, \"Invalid email address\" unless @domain.verification_email_addresses.include?(params[:email_address])\n\n# after\nunless @domain.verification_email_addresses.include?(params[:email_address].to_s.strip.downcase)\n  return respond_to do |wants|\n    wants.html { flash.now[:alert] = \"Choose one of the listed verification email addresses.\" }\n    wants.json { render json: { flash: { alert: \"Choose one of the listed verification email addresses.\" } }, status: :unprocessable_entity }\n  end\nend","handlingStrategy":"validation","validationCode":"# before POSTing the verify form / calling verify\naddress = params[:email_address].to_s.strip.downcase\nallowed = @domain.verification_email_addresses.map { |a| a.downcase }\nreturn redirect_back(alert: \"Choose one of the listed verification addresses.\") unless allowed.include?(address)","typeGuard":"def permitted_verification_address?(domain, candidate)\n  domain.verification_email_addresses.map(&:downcase).include?(candidate.to_s.strip.downcase)\nend","tryCatchPattern":"begin\n  # verify call with email_address\nrescue Postal::Error => e\n  # bad input from the user: re-render with a 4xx and an alert, do not retry\n  flash.now[:alert] = e.message\n  render :verify, status: :unprocessable_entity\nend","preventionTips":["Render the email-address choice as a closed dropdown sourced from verification_email_addresses, not a free-text input","Normalize (strip/downcase) the param before comparing against the generated list","When a domain is renamed, invalidate cached verify forms so stale addresses are never submitted","Prefer DNS-record verification when mailbox access is unreliable"],"tags":["postal","domains","verification","rails-controller","input-validation"],"backgroundTag":"input-validation-failed","analyzedSha":"d038eaa8c763d3cafa797ccd6f773d53470bd336","analyzedAt":"2026-08-21T13:52:57.446Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}