{"record":{"id":"5026d0b9fb56563d","repo":"we-promise/sure","slug":"validation-failed-5026d0","errorCode":"validation_failed","errorMessage":"Invalid #{param_name} format","messagePattern":"Invalid #(.+?) format","errorType":"validation","errorClass":"ArgumentError","httpStatus":422,"severity":"warning","filePath":"app/controllers/api/v1/holdings_controller.rb","lineNumber":100,"sourceCode":"    def safe_page_param\n      page = params[:page].to_i\n      page > 0 ? page : 1\n    end\n\n    def safe_per_page_param\n      per_page = params[:per_page].to_i\n      case per_page\n      when 1..100\n        per_page\n      else\n        25\n      end\n    end\n\n    def parse_date!(value, param_name)\n      Date.parse(value)\n    rescue Date::Error, ArgumentError, TypeError\n      raise ArgumentError, \"Invalid #{param_name} format\"\n    end\n\n    def render_validation_error(message, errors)\n      render json: {\n        error: \"validation_failed\",\n        message: message,\n        errors: errors\n      }, status: :unprocessable_entity\n    end\n\n    def log_and_render_error(action, exception)\n      Rails.logger.error \"HoldingsController##{action} error: #{exception.message}\"\n      Rails.logger.error exception.backtrace.join(\"\\n\")\n      render json: {\n        error: \"internal_server_error\",\n        message: \"An unexpected error occurred\"\n      }, status: :internal_server_error\n    end","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/controllers/api/v1/holdings_controller.rb#L82-L118","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Send dates as strict ISO 8601 YYYY-MM-DD (e.g. 2024-01-31)","Check every date param (date, start_date, end_date) is well-formed before the request","URL-encode values and strip stray whitespace/quotes","Prefer explicit formats: '2024-01-31' never '31/01/2024' since Date.parse is ambiguous, not strict"],"exampleFix":"# before\nGET /api/v1/holdings?start_date=01/31/2024\n# after\nGET /api/v1/holdings?start_date=2024-01-31","handlingStrategy":"validation","validationCode":"require 'date'\n%i[date start_date end_date].each do |k|\n  next if params[k].to_s.strip.empty?\n  Date.iso8601(params[k]) # raises Date::Error unless YYYY-MM-DD\nend","typeGuard":"def valid_iso_date?(v) = v.to_s.match?(/\\A\\d{4}-\\d{2}-\\d{2}\\z/) && !!Date.iso8601(v) rescue false","tryCatchPattern":"begin\n  client.get('/api/v1/holdings', params)\nrescue Faraday::UnprocessableEntity => e\n  # 422 validation_failed: e.response[:body]['errors'] lists 'Invalid <param> format'\nend","preventionTips":["Always emit YYYY-MM-DD from one shared formatter","Never send locale-formatted dates (MM/DD/YYYY) — Date.parse may misread them silently","Strip whitespace before sending query params"],"tags":["rails","date-parsing","validation","http-422","query-params"],"backgroundTag":"invalid-date-format","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}