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.

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)

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.