instructure/canvas-lms · error · PeerReview::InvalidDatesError

Due date cannot be before available from date

Error message

Due date cannot be before available from date

What it means

After parsing, validate_peer_review_dates enforces unlock_at < due_at <= lock_at. It raises PeerReview::InvalidDatesError with 'Due date cannot be before available from date' when due_at is earlier than unlock_at (and both are provided). This mirrors standard Canvas date ordering restrictions for the peer review sub assignment.

Solutions

  1. Ensure unlock_at is strictly earlier than due_at before calling the validation.
  2. Compare parsed dates client-side: raise/skip when due_at < unlock_at and correct the payload.
  3. If both dates were swapped by mistake, exchange the unlock_at and due_at values.

Example fix

// before
validate_peer_review_dates({ unlock_at: "2024-03-10T00:00:00Z", due_at: "2024-03-01T00:00:00Z" })
// raises
// after
validate_peer_review_dates({ unlock_at: "2024-03-01T00:00:00Z", due_at: "2024-03-10T00:00:00Z" })
Defensive patterns

Strategy: validation

Validate before calling

if dates[:due_at] && dates[:unlock_at] && dates[:due_at] < dates[:unlock_at]
  raise ArgumentError, "due_at must be after unlock_at"
end

Try / catch

begin
  validate_peer_review_dates(dates)
rescue PeerReview::InvalidDatesError => e
  render json: { error: e.message }, status: :unprocessable_entity
end

Prevention

When it happens

Trigger: Submitting peer_review_dates where due_at < unlock_at, e.g. due_at: Mar 1 with unlock_at: Mar 10; clients that swap the two fields; timezone conversions shifting due_at behind unlock_at.

Common situations: Users entering an 'available from' date after the due date in a form; API integrations computing dates from offset epochs; bulk updates applying one assignment's dates to another with a different schedule.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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

Appendix: source

Thrown at app/services/peer_review/validations.rb:91

      # 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")
    end

    if unlock_at && lock_at && unlock_at > lock_at
      raise PeerReview::InvalidDatesError, I18n.t("Available from date cannot be after until date")
    end
  end

  # Validates that peer review override dates fall within parent assignment override dates
  # assignment available from date <= assignment due date <= peer review available from date <= peer review due date <= peer review until date <= assignment until date
  def validate_override_dates_against_parent_override(peer_review_override, parent_override)
    parent_unlock_at = parent_override.unlock_at_overridden ? parent_override.unlock_at : nil
    parent_due_at = parent_override.due_at_overridden ? parent_override.due_at : nil
    parent_lock_at = parent_override.lock_at_overridden ? parent_override.lock_at : nil

View on GitHub (pinned to 1c9f0bb801)