{"record":{"id":"fcb992fc84965cba","repo":"we-promise/sure","slug":"key-must-be-an-iso-8601-date","errorCode":null,"errorMessage":"#{key} must be an ISO 8601 date","messagePattern":"#(.+?) must be an ISO 8601 date","errorType":"validation","errorClass":"Api::V1::SecurityResourceFiltering::InvalidFilterError","httpStatus":422,"severity":"warning","filePath":"app/controllers/concerns/api/v1/security_resource_filtering.rb","lineNumber":51,"sourceCode":"    end\n\n    def parse_boolean_filter_param(key)\n      normalized_value = params[key].to_s.strip.downcase\n\n      invalid_filter!(\"#{key} must be true or false\") if normalized_value.blank?\n      return BOOLEAN_FILTERS.fetch(normalized_value) if BOOLEAN_FILTERS.key?(normalized_value)\n\n      invalid_filter!(\"#{key} must be true or false\")\n    end\n\n    def parse_date_param(key)\n      Date.iso8601(params[key].to_s)\n    rescue ArgumentError\n      invalid_filter!(\"#{key} must be an ISO 8601 date\")\n    end\n\n    def invalid_filter!(message)\n      raise InvalidFilterError, message\n    end\nend\n","sourceCodeStart":33,"sourceCodeEnd":54,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/controllers/concerns/api/v1/security_resource_filtering.rb#L33-L54","documentation":"Api::V1::SecurityResourceFiltering (app/controllers/concerns/api/v1/security_resource_filtering.rb:47-52) supplies parse_date_param to the securities and security_prices index actions. It uses strict Date.iso8601; failures raise ArgumentError, converted by invalid_filter! into InvalidFilterError '<key> must be an ISO 8601 date', which each controller rescues to 422 validation_failed.","triggerScenarios":"GET /api/v1/securities?start_date=2024/01/31 or GET /api/v1/security_prices?end_date=Jan 31 — any date filter on those endpoints that is not strict YYYY-MM-DD.","commonSituations":"Shared client code sending US or slash-formatted dates to every index endpoint uniformly; spreadsheet-exported dates with locale formatting; timestamps reused from holdings' more lenient Date.parse behavior.","solutions":["Send date filters as strict YYYY-MM-DD on /securities and /security_prices","Centralize one date-formatting helper that always emits ISO 8601 dates","Validate with Date.iso8601 client-side before the request","Read the 422 message — it names the exact failing param"],"exampleFix":"# before\nGET /api/v1/securities?start_date=31-01-2024\n# after\nGET /api/v1/securities?start_date=2024-01-31","handlingStrategy":"validation","validationCode":"require 'date'\n%i[start_date end_date].each do |k|\n  next if filters[k].to_s.strip.empty?\n  Date.iso8601(filters[k]) # strict YYYY-MM-DD or raise\nend","typeGuard":"def valid_iso_date?(v) = v.to_s.match?(/\\A\\d{4}-\\d{2}-\\d{2}\\z/)","tryCatchPattern":"begin\n  client.get('/api/v1/securities', filters)\nrescue Faraday::UnprocessableEntity => e\n  # 422 — body message says '<key> must be an ISO 8601 date'\nend","preventionTips":["One date-format helper for every list endpoint","Normalize locale formats at the client boundary","Test filter formatting with non-padded dates — they must fail fast client-side"],"tags":["rails","query-params","validation","http-422","iso8601","date","concern"],"backgroundTag":"invalid-date-format","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}