instructure/canvas-lms · error · ArgumentError

Date must be in YYYY-MM-DD format

Error message

Date must be in YYYY-MM-DD format

What it means

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.

Solutions

  1. Pass Date objects instead of strings
  2. Normalize with Date.parse(value).iso8601 before calling
  3. For timestamps, take the date part: timestamp.to_date.iso8601
  4. Validate controller params with a YYYY-MM-DD format check

Example fix

// before
service.call('2026-09-15T10:00:00Z', end_date, user, format)
// after
service.call(Date.parse('2026-09-15T10:00:00Z').iso8601, end_date, user, format)
Defensive patterns

Strategy: validation

Validate before calling

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/)

Type guard

def date_only?(value)
  value.is_a?(Date) || (value.is_a?(String) && value.match?(/\A\d{4}-\d{2}-\d{2}\z/))
end

Try / catch

begin
  service.call(start_date, end_date, user, format)
rescue ArgumentError => e
  Rails.logger.warn("invalid page view query dates: #{e.message}")
end

Prevention

When it happens

Trigger: 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.

Common situations: Copying dates from logs including time; localized input formats; JSON payloads carrying full ISO 8601 timestamps; single-digit months/days.

Related errors


AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15). Data as JSON: /api/errors/40f155c82749c138. Report an issue: GitHub.

Appendix: source

Thrown at app/services/page_views/enqueue_query_service.rb:44

      start_date = parse_date_only(start_date) unless start_date.is_a?(Date)
      end_date = parse_date_only(end_date) unless end_date.is_a?(Date)
      request = Common::AsyncQueryRequest.new(start_date:, end_date:, user_id: user.global_id, root_account_uuids: cached_root_account_uuids_for(user:), format:)
      request.validate!
      CanvasHttp.post(
        uri.to_s,
        request_headers,
        content_type: "application/json",
        body: request.to_json
      ) do |response|
        handle_generic_errors(response) unless response.code.to_i == 201
        return response.header["Location"].split("/").last
      end
    end

    private

    def parse_date_only(date_string)
      raise ArgumentError, "Date must be in YYYY-MM-DD format" unless date_string.match?(/\A\d{4}-\d{2}-\d{2}\z/)

      Date.parse(date_string)
    end

    def cached_root_account_uuids_for(user:)
      user.shard.activate do
        user.root_account_ids.map do |id|
          Account.find_cached(id).uuid
        end
      end
    end
  end
end

View on GitHub (pinned to 1c9f0bb801)