{"record":{"id":"55f36d27a229c856","repo":"instructure/canvas-lms","slug":"date-must-be-in-yyyy-mm-dd-format","errorCode":null,"errorMessage":"Date must be in YYYY-MM-DD format","messagePattern":"Date must be in YYYY-MM-DD format","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"app/services/page_views/enqueue_batch_query_service.rb","lineNumber":66,"sourceCode":"        content_type: \"application/json\",\n        body: request.to_json\n      ) do |response|\n        handle_generic_errors(response) unless response.code.to_i == 201\n        return response.header[\"Location\"].split(\"/\").last\n      end\n    end\n\n    private\n\n    def validate_users!(users)\n      raise ArgumentError, \"users must be an array\" unless users.is_a?(Array)\n      raise ArgumentError, \"users cannot be empty\" if users.empty?\n\n      raise ArgumentError, \"all elements must be User objects\" unless users.all?(User)\n    end\n\n    def parse_date_only(date_string)\n      raise ArgumentError, \"Date must be in YYYY-MM-DD format\" unless date_string.match?(/\\A\\d{4}-\\d{2}-\\d{2}\\z/)\n\n      Date.parse(date_string)\n    end\n\n    def collect_root_account_uuids(users)\n      users.flat_map do |user|\n        user.shard.activate do\n          user.root_account_ids.map do |id|\n            Account.find_cached(id).uuid\n          end\n        end\n      end.uniq\n    end\n  end\nend\n","sourceCodeStart":48,"sourceCodeEnd":82,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/services/page_views/enqueue_batch_query_service.rb#L48-L82","documentation":"PageViews::EnqueueBatchQueryService#parse_date_only raises ArgumentError when a date string does not match the strict YYYY-MM-DD pattern (/\\A\\d{4}-\\d{2}-\\d{2}\\z/). The remote page views API expects date-only strings, so the service validates format before parsing with Date.parse. Both start_date and end_date go through this when they are not already Date objects.","triggerScenarios":"Passing start_date or end_date as '09/15/2026', '2026-9-5', '2026-09-15T00:00:00Z', or any string with extra whitespace/characters to call.","commonSituations":"User-supplied date input in a different locale format; timestamps including time components; single-digit month/day without zero padding; trailing newline from file/env input.","solutions":["Normalize the string to YYYY-MM-DD (zero-padded) before calling","Pass Date objects directly instead of strings (the service skips parsing for Date instances)","Use Date.parse(input).iso8601 in the caller to normalize","Strip the time component: Time.zone.now.to_date.iso8601"],"exampleFix":"// before\nservice.call('09/15/2026', '09/20/2026', users, format)\n// after\nservice.call(Date.new(2026, 9, 15), Date.new(2026, 9, 20), users, format)","handlingStrategy":"validation","validationCode":"DATE_RE = /\\A\\d{4}-\\d{2}-\\d{2}\\z/\nraise ArgumentError, 'start_date must be YYYY-MM-DD or Date' unless start_date.is_a?(Date) || start_date.to_s.match?(DATE_RE)","typeGuard":"def date_only?(value)\n  value.is_a?(Date) || (value.is_a?(String) && value.match?(/\\A\\d{4}-\\d{2}-\\d{2}\\z/))\nend","tryCatchPattern":"begin\n  service.call(start_date, end_date, users, format)\nrescue ArgumentError => e\n  raise \"invalid date argument: #{e.message}\"\nend","preventionTips":["Pass Date objects to avoid string parsing entirely","Zero-pad months and days in generated strings","Strip time components with .to_date before formatting","Normalize user input with Date.parse(...).iso8601"],"tags":["ruby","date-format","argument-validation"],"backgroundTag":"invalid-date-format","analyzedSha":"1c9f0bb8013ed69c4f2efe11fd483025469b7e6c","analyzedAt":"2026-09-15T20:33:18.891Z","contentChangedAt":"2026-09-15T20:33:18.891Z","schemaVersion":2},"datasetVersion":"2026-09-23T02:17:17.105Z"}