Freika/dawarich · error · ArgumentError

Invalid date format: %{value}

Error message

Invalid date format: %{value}

What it means

ArgumentError raised inside parse_date_parameter when a String date parameter is not purely digits and Time.zone.parse returns nil - ActiveSupport could not interpret it as a datetime at all. The %{value} placeholder carries the offending string. Note this raise happens inside a method that immediately rescues ArgumentError and re-raises from line 89, so the observable failure is the identical invalid_date message from the rescue clause.

Source

Thrown at app/services/maps/bounds_calculator.rb:78

      }
    end

    def build_no_data_response
      {
        success: false,
        error: I18n.t('services.maps.bounds_calculator.no_data_found_for_the_specified_date_range'),
        point_count: 0
      }
    end

    def parse_date_parameter(param)
      case param
      when String
        if param.match?(/^\d+$/)
          param.to_i
        else
          parsed_time = Time.zone.parse(param)
          raise ArgumentError, I18n.t('services.maps.bounds_calculator.invalid_date', value: param) if parsed_time.nil?

          parsed_time.to_i
        end
      when Integer
        param
      else
        param.to_i
      end
    rescue ArgumentError => e
      Rails.logger.error "Invalid date format: #{param} - #{e.message}"
      raise ArgumentError, I18n.t('services.maps.bounds_calculator.invalid_date', value: param)
    end
  end
end

View on GitHub (pinned to 97fad417c5)

Solutions

  1. Send dates as unix epoch timestamps (integer or digit-only string) - the unambiguous fast path
  2. Or use ISO 8601 strings like '2024-06-01T00:00:00Z' that Time.zone.parse always handles
  3. Validate/normalize date inputs on the client before issuing the request
  4. Rescue ArgumentError at the controller and return 400 with the message rather than letting it bubble as 500

Example fix

# before
calculator = Maps::BoundsCalculator.new(user: u, start_date: 'June 1 2024', end_date: 'todayish')

# after
calculator = Maps::BoundsCalculator.new(user: u, start_date: 1717200000, end_date: 1719791999)
Defensive patterns

Strategy: validation

Validate before calling

def parseable_bound_date?(value)
  case value
  when Integer then true
  when String then value.match?(/\A\d+\z/) || !Time.zone.parse(value).nil?
  else false
  end
end

raise ArgumentError, "bad date #{start_date}" unless parseable_bound_date?(start_date)

Type guard

def epoch_or_iso?(value)
  value.is_a?(Integer) || value.to_s.match?(/\A\d+\z\z/) || value.to_s.match?(\A\d{4}-\d{2}-\d{2}(T[\d:.]+Z)?\z/)
end

Try / catch

begin
  result = Maps::BoundsCalculator.new(user: u, start_date: s, end_date: e).call
rescue ArgumentError => e
  render json: { error: e.message }, status: :bad_request  # message includes the offending %{value}
end

Prevention

When it happens

Trigger: Passing date strings like 'not-a-date', '2024-13-01' (month 13 makes parse return nil in many zones), 'YYYY/MM/DD' variants ActiveSupport cannot resolve, or URL-encoding damage that mangles the parameter. Digit strings ('1717200000') and Integers never hit this path.

Common situations: Hand-crafted API calls, locale-specific formats (DD/MM/YYYY vs MM/DD/YYYY ambiguity), or test fixtures using placeholder strings like 'foo'.

Related errors


AI-assisted analysis of Freika/dawarich@97fad417c5 (2026-08-21). Data as JSON: /api/errors/c32da336fb14e4c3. Report an issue: GitHub.