we-promise/sure · warning · ArgumentError

validation_failed

validation_failed

Error message

Invalid #{param_name} format

What it means

HoldingsController#parse_date! (app/controllers/api/v1/holdings_controller.rb:97-100) wraps Ruby's lenient Date.parse for the date, start_date and end_date query params. When the value cannot be parsed, Ruby raises Date::Error/ArgumentError/TypeError and the controller re-raises ArgumentError with 'Invalid <param> format'. The index action rescues ArgumentError and renders 422 validation_failed with the message in both message and errors.

Source

Thrown at app/controllers/api/v1/holdings_controller.rb:100

    def safe_page_param
      page = params[:page].to_i
      page > 0 ? page : 1
    end

    def safe_per_page_param
      per_page = params[:per_page].to_i
      case per_page
      when 1..100
        per_page
      else
        25
      end
    end

    def parse_date!(value, param_name)
      Date.parse(value)
    rescue Date::Error, ArgumentError, TypeError
      raise ArgumentError, "Invalid #{param_name} format"
    end

    def render_validation_error(message, errors)
      render json: {
        error: "validation_failed",
        message: message,
        errors: errors
      }, status: :unprocessable_entity
    end

    def log_and_render_error(action, exception)
      Rails.logger.error "HoldingsController##{action} error: #{exception.message}"
      Rails.logger.error exception.backtrace.join("\n")
      render json: {
        error: "internal_server_error",
        message: "An unexpected error occurred"
      }, status: :internal_server_error
    end

View on GitHub (pinned to e69894adb9)

Solutions

  1. Send dates as strict ISO 8601 YYYY-MM-DD (e.g. 2024-01-31)
  2. Check every date param (date, start_date, end_date) is well-formed before the request
  3. URL-encode values and strip stray whitespace/quotes
  4. Prefer explicit formats: '2024-01-31' never '31/01/2024' since Date.parse is ambiguous, not strict

Example fix

# before
GET /api/v1/holdings?start_date=01/31/2024
# after
GET /api/v1/holdings?start_date=2024-01-31
Defensive patterns

Strategy: validation

Validate before calling

require 'date'
%i[date start_date end_date].each do |k|
  next if params[k].to_s.strip.empty?
  Date.iso8601(params[k]) # raises Date::Error unless YYYY-MM-DD
end

Type guard

def valid_iso_date?(v) = v.to_s.match?(/\A\d{4}-\d{2}-\d{2}\z/) && !!Date.iso8601(v) rescue false

Try / catch

begin
  client.get('/api/v1/holdings', params)
rescue Faraday::UnprocessableEntity => e
  # 422 validation_failed: e.response[:body]['errors'] lists 'Invalid <param> format'
end

Prevention

When it happens

Trigger: GET /api/v1/holdings?date=not-a-date, ?start_date=2024-13-01 (month 13), or any non-date string for date/start_date/end_date. Empty values are skipped because of the .present? guard, so only malformed non-empty values trigger it.

Common situations: Sending locale-specific formats like '31/01/2024' or 'Jan 31, 2024' — note Date.parse may silently 'succeed' with the wrong interpretation for ambiguous inputs, which is worse than the error; passing a datetime string where only the date part is wanted; copy/paste artifacts like quotes or trailing spaces in the URL.

Related errors


AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21). Data as JSON: /api/errors/5026d0b9fb56563d. Report an issue: GitHub.