postalserver/postal · error · Postal::Error

Invalid email address

Error message

Invalid email address

What it means

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.

Solutions

  1. Submit one of the exact addresses exposed by the domain's verification_email_addresses (the values the UI dropdown offers)
  2. Strip and normalize params[:email_address] (e.g. .to_s.strip.downcase) before comparing, if the mismatch is only whitespace/case
  3. If the domain was renamed, reload the verify page so the form uses the newly generated addresses
  4. 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
  5. Verify the domain via the DNS/records method instead of the email method

Example fix

# before
raise Postal::Error, "Invalid email address" unless @domain.verification_email_addresses.include?(params[:email_address])

# after
unless @domain.verification_email_addresses.include?(params[:email_address].to_s.strip.downcase)
  return respond_to do |wants|
    wants.html { flash.now[:alert] = "Choose one of the listed verification email addresses." }
    wants.json { render json: { flash: { alert: "Choose one of the listed verification email addresses." } }, status: :unprocessable_entity }
  end
end
Defensive patterns

Strategy: validation

Validate before calling

# before POSTing the verify form / calling verify
address = params[:email_address].to_s.strip.downcase
allowed = @domain.verification_email_addresses.map { |a| a.downcase }
return redirect_back(alert: "Choose one of the listed verification addresses.") unless allowed.include?(address)

Type guard

def permitted_verification_address?(domain, candidate)
  domain.verification_email_addresses.map(&:downcase).include?(candidate.to_s.strip.downcase)
end

Try / catch

begin
  # verify call with email_address
rescue Postal::Error => e
  # bad input from the user: re-render with a 4xx and an alert, do not retry
  flash.now[:alert] = e.message
  render :verify, status: :unprocessable_entity
end

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of postalserver/postal@d038eaa8c7 (2026-08-21). Data as JSON: /api/errors/74df7bf8c9fd99ef. Report an issue: GitHub.

Appendix: source

Thrown at app/controllers/domains_controller.rb:83

      else
        respond_to do |wants|
          wants.html { flash.now[:alert] = "We couldn't verify your domain. Please double check you've added the TXT record correctly." }
          wants.json { render json: { flash: { alert: "We couldn't verify your domain. Please double check you've added the TXT record correctly." } } }
        end
      end
    when "Email"
      if params[:code]
        if @domain.verification_token == params[:code].to_s.strip
          @domain.mark_as_verified
          redirect_to_with_json [:setup, organization, @server, @domain], notice: "#{@domain.name} has been verified successfully. You now need to configure your DNS records."
        else
          respond_to do |wants|
            wants.html { flash.now[:alert] = "Invalid verification code. Please check and try again." }
            wants.json { render json: { flash: { alert: "Invalid verification code. Please check and try again." } } }
          end
        end
      elsif params[:email_address].present?
        raise Postal::Error, "Invalid email address" unless @domain.verification_email_addresses.include?(params[:email_address])

        AppMailer.verify_domain(@domain, params[:email_address], current_user).deliver
        if @domain.owner.is_a?(Server)
          redirect_to_with_json verify_organization_server_domain_path(organization, @server, @domain, email_address: params[:email_address])
        else
          redirect_to_with_json verify_organization_domain_path(organization, @domain, email_address: params[:email_address])
        end
      end
    end
  end

  def setup
    return if @domain.verified?

    redirect_to [:verify, organization, @server, @domain], alert: "You can't set up DNS for this domain until it has been verified."
  end

  def check

View on GitHub (pinned to d038eaa8c7)