Shopify/liquid · error · Liquid::FloatDomainError

e.message (re-raised as Liquid::FloatDomainError from…

Error message

e.message (re-raised as Liquid::FloatDomainError from ::FloatDomainError in ceil)

What it means

The `ceil` filter wraps Ruby's ::FloatDomainError and re-raises it as Liquid::FloatDomainError. It is raised when the input number is NaN or Infinity, values with no integer ceiling. Liquid re-raises the native error under its own class for consistent template error handling.

Solutions

  1. Ensure the divisor feeding any upstream float division is a non-zero integer or validated float.
  2. Guard the input in the template before applying ceil.
  3. Rescue Liquid::FloatDomainError in the error renderer and output a sensible default (e.g. 1 page).
  4. Validate external data: reject Infinity/NaN from API/JSON payloads before template rendering.
  5. Add tests covering per_page = 0 and empty totals for pagination templates.

Example fix

// before
{% assign pages = total | divided_by: per_page | ceil %}
// after
{% if per_page > 0 %}{% assign pages = total | divided_by: per_page | ceil %}{% else %}{% assign pages = 1 %}{% endif %}
Defensive patterns

Strategy: validation

Validate before calling

# before render
raise "non-finite input for ceil" unless input.nil? || input.to_f.finite?

Type guard

def ceil_safe(v)
  v.is_a?(Numeric) && v.to_f.finite? ? v.to_f.ceil : 0
end

Try / catch

begin
  template.render(assigns)
rescue Liquid::FloatDomainError => e
  output_default_page_count
end

Prevention

When it happens

Trigger: `{{ value | ceil }}` where value coerces to Float::INFINITY or Float::NAN, typically after a prior `divided_by: 0.0` in the same pipeline.

Common situations: Pagination page counts (`{{ total | divided_by: per_page | ceil }}`) when per_page is 0.0 or a float zero; dashboards computing ceilings of averaged metrics with bad denominators.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of Shopify/liquid@807d45a6b3 (2026-09-08). Data as JSON: /api/errors/3854cbff4f185ddc. Report an issue: GitHub.

Appendix: source

Thrown at lib/liquid/standardfilters.rb:908

      result = Utils.to_number(input).round(Utils.to_number(n))
      result = result.to_f if result.is_a?(BigDecimal)
      result = result.to_i if n == 0
      result
    rescue ::FloatDomainError => e
      raise Liquid::FloatDomainError, e.message
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category math
    # @liquid_summary
    #   Rounds a number up to the nearest integer.
    # @liquid_syntax number | ceil
    # @liquid_return [number]
    def ceil(input)
      Utils.to_number(input).ceil.to_i
    rescue ::FloatDomainError => e
      raise Liquid::FloatDomainError, e.message
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category math
    # @liquid_summary
    #   Rounds a number down to the nearest integer.
    # @liquid_syntax number | floor
    # @liquid_return [number]
    def floor(input)
      Utils.to_number(input).floor.to_i
    rescue ::FloatDomainError => e
      raise Liquid::FloatDomainError, e.message
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category math

View on GitHub (pinned to 807d45a6b3)