instructure/canvas-lms · error · PeerReview::InvalidDatesError
Invalid datetime format for
Error message
Invalid datetime format for %{attribute} What it means
PeerReview::Validations#validate_peer_review_dates raises PeerReview::InvalidDatesError when a provided date field (unlock_at/due_at/lock_at) is a String that does not match Api::ISO8601_REGEX. The method accepts Time objects or ISO8601 strings; anything else in string form is rejected with the offending attribute named in the message.
Solutions
- Convert strings to ISO8601 (e.g. time.iso8601 or date.to_time.utc.iso8601) before passing.
- Pass Time/DateTime objects instead of strings to skip string parsing entirely.
- Pre-validate with Api::ISO8601_REGEX.match?(value) client-side and reject bad input early.
Example fix
// before
PeerReview::Validator.validate_peer_review_dates({ due_at: "03/01/2024 5pm" })
// after
PeerReview::Validator.validate_peer_review_dates({ due_at: Time.zone.parse("2024-03-01 17:00").iso8601 }) Defensive patterns
Strategy: validation
Validate before calling
def valid_peer_review_date?(v) v.respond_to?(:zone) || (v.is_a?(String) && Api::ISO8601_REGEX.match?(v)) end
Type guard
def coerce_to_time(v) = v.is_a?(String) ? Time.zone.parse(v) : v
Try / catch
begin
validate_peer_review_dates(dates)
rescue PeerReview::InvalidDatesError => e
render json: { error: e.message }, status: :bad_request
end Prevention
- Always serialize dates with .iso8601
- Pass Time objects instead of strings when possible
- Pre-validate user date input at the form/API boundary
When it happens
Trigger: Passing dates like "2024-03-01 10:00" (space instead of T), "03/01/2024", or "2024-03-01T10:00:00" with an offset format the regex rejects to peer_review_dates; localizing date strings before sending them.
Common situations: API clients using strftime with human-readable formats; form params passed through unprocessed; JS Date.toString() output sent directly to the endpoint.
Related errors
- Due date cannot be before available from date
- Invalid parent assignment
- Must be a group assignment to create group overrides
- Peer review sub assignment does not exist
- Peer review sub assignment exists
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/8526c2e2be3d6fcb.
Report an issue: GitHub.
Appendix: source
Thrown at app/services/peer_review/validations.rb:77
def validate_peer_review_sub_assignment_not_exist(assignment)
if assignment.peer_review_sub_assignment.present?
raise PeerReview::SubAssignmentExistsError, I18n.t("Peer review sub assignment exists")
end
end
# Validates that peer review dates follow the restriction below
# peer review unlock_at < peer review due_at <= peer review lock_at
def validate_peer_review_dates(peer_review_dates)
parsed_dates = {}
%w[due_at unlock_at lock_at].each do |date_field|
date_value = peer_review_dates.fetch(date_field.to_sym, nil)
next unless date_value.present?
# Accept both Time objects and ISO8601 strings for API compatibility
if date_value.is_a?(String)
unless Api::ISO8601_REGEX.match?(date_value)
raise PeerReview::InvalidDatesError, I18n.t("Invalid datetime format for %{attribute}", attribute: date_field)
end
parsed_dates[date_field.to_sym] = Time.zone.parse(date_value)
else
parsed_dates[date_field.to_sym] = date_value
end
end
due_at = parsed_dates[:due_at]
unlock_at = parsed_dates[:unlock_at]
lock_at = parsed_dates[:lock_at]
if due_at && unlock_at && due_at < unlock_at
raise PeerReview::InvalidDatesError, I18n.t("Due date cannot be before available from date")
end
if due_at && lock_at && due_at > lock_at
raise PeerReview::InvalidDatesError, I18n.t("Due date cannot be after until date")View on GitHub (pinned to 1c9f0bb801)