{"record":{"id":"40f155c82749c138","repo":"instructure/canvas-lms","slug":"date-must-be-in-yyyy-mm-dd-format-enqueue-query-service","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_query_service.rb","lineNumber":44,"sourceCode":"      start_date = parse_date_only(start_date) unless start_date.is_a?(Date)\n      end_date = parse_date_only(end_date) unless end_date.is_a?(Date)\n      request = Common::AsyncQueryRequest.new(start_date:, end_date:, user_id: user.global_id, root_account_uuids: cached_root_account_uuids_for(user:), format:)\n      request.validate!\n      CanvasHttp.post(\n        uri.to_s,\n        request_headers,\n        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 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 cached_root_account_uuids_for(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\n  end\nend\n","sourceCodeStart":26,"sourceCodeEnd":58,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/services/page_views/enqueue_query_service.rb#L26-L58","documentation":"PageViews::EnqueueQueryService#parse_date_only raises ArgumentError when a date string does not match the strict YYYY-MM-DD regex (/\\A\\d{4}-\\d{2}-\\d{2}\\z/). This mirrors the batch service: the page views API requires date-only values, so strings are validated before Date.parse. Only applied when the argument is not already a Date.","triggerScenarios":"Passing start_date or end_date as '15-09-2026', '2026/09/15', an ISO timestamp '2026-09-15T10:00:00Z', or a string with whitespace to call.","commonSituations":"Copying dates from logs including time; localized input formats; JSON payloads carrying full ISO 8601 timestamps; single-digit months/days.","solutions":["Pass Date objects instead of strings","Normalize with Date.parse(value).iso8601 before calling","For timestamps, take the date part: timestamp.to_date.iso8601","Validate controller params with a YYYY-MM-DD format check"],"exampleFix":"// before\nservice.call('2026-09-15T10:00:00Z', end_date, user, format)\n// after\nservice.call(Date.parse('2026-09-15T10:00:00Z').iso8601, end_date, user, format)","handlingStrategy":"validation","validationCode":"raise ArgumentError, 'date must be YYYY-MM-DD or Date' unless start_date.is_a?(Date) || start_date.to_s.match?(/\\A\\d{4}-\\d{2}-\\d{2}\\z/)","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, user, format)\nrescue ArgumentError => e\n  Rails.logger.warn(\"invalid page view query dates: #{e.message}\")\nend","preventionTips":["Pass Date objects directly when you already have them","Convert timestamps with .to_date.iso8601","Validate date params at the controller boundary with a format regex","Avoid locale-dependent date formatting before API calls"],"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"}