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

Available from date cannot be after until date

Error message

Available from date cannot be after until date

What it means

PeerReview::InvalidDatesError raised by validate_peer_review_dates when the peer review sub-assignment's unlock_at (available from) is later than its lock_at (until date). An availability window whose start exceeds its end is meaningless, so Canvas rejects it before persisting. Thrown whenever peer review dates are validated, both standalone and against the parent assignment.

Solutions

  1. Ensure unlock_at <= lock_at: raise lock_at or lower unlock_at in the payload
  2. If reviews should be open-ended, drop lock_at (nil) instead of leaving a stale early value
  3. If reviews should not be available yet, drop unlock_at or set it before lock_at
  4. Pre-validate: raise unless Time.parse(unlock_at) <= Time.parse(lock_at)

Example fix

// before
{ unlock_at: '2026-06-01T00:00:00Z', lock_at: '2026-05-01T00:00:00Z' }
// after
{ unlock_at: '2026-05-01T00:00:00Z', lock_at: '2026-06-01T00:00:00Z' }
Defensive patterns

Strategy: validation

Validate before calling

def ordered_window?(dates)
  u = dates[:unlock_at]; l = dates[:lock_at]
  return true if u.nil? || l.nil?
  u = Time.zone.parse(u) if u.is_a?(String)
  l = Time.zone.parse(l) if l.is_a?(String)
  u <= l
end

Type guard

def parse_iso_date(v)
  return v unless v.is_a?(String)
  raise ArgumentError, 'not ISO8601' unless Api::ISO8601_REGEX.match?(v)
  Time.zone.parse(v)
end

Try / catch

begin
  validator.validate_peer_review_dates(dates)
rescue PeerReview::InvalidDatesError => e
  Rails.logger.warn("peer review date order rejected: #{e.message}")
  flash[:error] = e.message
end

Prevention

When it happens

Trigger: Submitting peer review dates where both unlock_at and lock_at are present and unlock_at > lock_at, e.g. unlock_at: '2026-06-01T00:00:00Z', lock_at: '2026-05-01T00:00:00Z', via the peer review sub assignment create/update API path.

Common situations: Bulk date-import scripts that set unlock_at to a new semester start but forget to push lock_at; UI edits that advance the available-from date without re-checking the until date; copying dates from another course with a later start.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

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

      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

    validate_dates_within_parent_boundaries(
      child_dates: peer_review_override,
      parent_unlock_at:,
      parent_due_at:,
      parent_lock_at:,
      is_override: true
    )
  end

View on GitHub (pinned to 1c9f0bb801)