{"record":{"id":"a5c018745daec4bb","repo":"instructure/canvas-lms","slug":"invalid-date-format-for-field-value","errorCode":null,"errorMessage":"Invalid date format for %{field}: %{value}","messagePattern":"Invalid date format for %(.+?): %(.+?)","errorType":"validation","errorClass":"PeerReview::InvalidDatesError","httpStatus":null,"severity":"error","filePath":"app/services/peer_review/validations.rb","lineNumber":138,"sourceCode":"    validate_peer_review_dates(peer_review_dates)\n\n    validate_dates_within_parent_boundaries(\n      child_dates: peer_review_dates,\n      parent_unlock_at: parent_assignment.unlock_at,\n      parent_due_at: parent_assignment.due_at,\n      parent_lock_at: parent_assignment.lock_at,\n      is_override: false\n    )\n  end\n\n  def parse_date(dates_hash, field)\n    date_value = dates_hash.fetch(field, nil)\n    return nil unless date_value.present?\n\n    if date_value.is_a?(String)\n      parsed = Time.zone.parse(date_value)\n      if parsed.nil?\n        raise PeerReview::InvalidDatesError,\n              I18n.t(\"Invalid date format for %{field}: %{value}\", field:, value: date_value)\n      end\n      parsed\n    else\n      date_value\n    end\n  end\n\n  def validate_dates_within_parent_boundaries(child_dates:, parent_unlock_at:, parent_due_at:, parent_lock_at:, is_override: false)\n    child_unlock_at = parse_date(child_dates, :unlock_at)\n    child_due_at = parse_date(child_dates, :due_at)\n    child_lock_at = parse_date(child_dates, :lock_at)\n\n    # Validate: parent unlock_at <= parent due_at\n    if parent_unlock_at && parent_due_at && parent_unlock_at > parent_due_at\n      raise PeerReview::InvalidDatesError,\n            is_override ? I18n.t(\"Parent override due date cannot be before parent override available from date\") : I18n.t(\"Assignment due date cannot be before assignment available from date\")\n    end","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/services/peer_review/validations.rb#L120-L156","documentation":"PeerReview::InvalidDatesError raised by parse_date when a date field in the child dates hash is a String that Time.zone.parse cannot convert to a Time (returns nil). Unlike validate_peer_review_dates (which uses Api::ISO8601_REGEX), parse_date relies on Rails' time parser, so only strings producing nil fail here — unparseable garbage, not merely non-ISO8601 formats. Thrown from validate_dates_within_parent_boundaries during peer review vs parent date validation.","triggerScenarios":"Calling validate_peer_review_dates_against_parent_assignment or validate_override_dates_against_parent_override with a dates hash containing e.g. due_at: 'not-a-date', '32/13/2026', or another string Time.zone.parse cannot interpret.","commonSituations":"API clients sending localized date formats ('01/02/2026' ambiguity aside, some locales produce nil); empty-ish strings that pass present? but fail parsing; deserialization bugs delivering placeholder text like 'null' or 'TBD' as the date value.","solutions":["Send ISO8601/ISO8601-formatted UTC timestamps, e.g. '2026-05-01T00:00:00Z'","Pre-parse strings to Time objects client-side before passing the dates hash","Log the %{field} and %{value} in the message to find the offending key and fix the producer","Sanitize inputs: drop blank or non-date strings before calling the validator"],"exampleFix":"// before\nvalidate_peer_review_dates_against_parent_assignment({ due_at: 'March 3rd, 2026 sometime' }, assignment)\n// after\nvalidate_peer_review_dates_against_parent_assignment({ due_at: '2026-03-03T00:00:00Z' }, assignment)","handlingStrategy":"type-guard","validationCode":"def ensure_parseable_dates(dates)\n  dates.each do |field, value|\n    next if value.nil? || value.is_a?(Time)\n    raise ArgumentError, \"#{field} unparseable: #{value}\" if Time.zone.parse(value).nil?\n  end\nend","typeGuard":"def coercible_date?(v)\n  v.is_a?(Time) || (v.is_a?(String) && !Time.zone.parse(v).nil?)\nend","tryCatchPattern":"begin\n  validator.validate_peer_review_dates_against_parent_assignment(dates, assignment)\nrescue PeerReview::InvalidDatesError => e\n  if e.message.start_with?('Invalid date format')\n    bad = e.message[/value: (.+)$/, 1]\n    dates.reject! { |_, v| v == bad }\n    retry\n  end\n  raise\nend","preventionTips":["Always transmit ISO8601 timestamps ('2026-05-01T00:00:00Z')","Never send localized or human-formatted date strings to the API","Coerce strings to Time objects at the edge of your integration","Scrub placeholder values ('TBD', 'null', '') out of date fields before submission"],"tags":["peer-review","date-parsing","rails","canvas-lms"],"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"}