instructure/canvas-lms · error · Pv4Timeout

failed to load page view history due to service timeout

Error message

failed to load page view history due to service timeout

What it means

Pv4Client#fetch rescues Net::ReadTimeout and re-raises it as Pv4Timeout with a user-facing message. It means the PV4 page-view service did not respond within the HTTP read timeout while loading page-view history.

Solutions

  1. Retry the request, possibly narrowing the date range (oldest/newest) to reduce response size
  2. Increase the PV4 client read/open timeout configuration if queries legitimately take longer
  3. Check PV4 service health/scaling and network path between services
  4. Surface a friendly 'history temporarily unavailable' message and allow the user to retry

Example fix

// before
PageView.for_user(user, oldest: 2.years.ago)
// after
begin
  PageView.for_user(user, oldest: 2.years.ago)
rescue Pv4Timeout
  PageView.for_user(user, oldest: 1.month.ago)
end
Defensive patterns

Strategy: retry

Try / catch

begin
  views = PageView.for_user(user, oldest: range_start)
rescue Pv4Timeout
  views = PageView.for_user(user, oldest: range_start + 6.months)
rescue Pv4Timeout
  views = []
end

Prevention

When it happens

Trigger: Calling Pv4Client#fetch (via PageView.for_user) when the PV4 service is slow, overloaded, or the query is very large (long time range / high-volume user), exceeding the configured read timeout.

Common situations: PV4 service degradation or outage; querying history for a very active user over a long window; network latency between Canvas and PV4; timeout configured too aggressively for the deployment.

Understand the failure class

Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.

Related errors


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

Appendix: source

Thrown at app/models/page_view/pv4_client.rb:99

                    elsif http_request.present?
                      "#{HostUrl.protocol}:#{http_request}"
                    elsif vhost.present?
                      "#{HostUrl.protocol}://#{vhost}"
                    end
        pv["context_id"] = pv.delete("canvas_context_id")
        pv["context_type"] = pv.delete("canvas_context_type")
        pv["updated_at"] = pv["created_at"] = pv.delete("timestamp")
        pv["user_agent"] = pv.delete("agent").presence
        pv["account_id"] = pv.delete("root_account_id")
        pv["remote_ip"] = pv.delete("client_ip")
        pv["render_time"] = pv.delete("microseconds").to_f / 1_000_000
        pv["http_method"].try(:downcase!)
        pv["developer_key_id"] = pv.delete("developer_key_id")

        PageView.from_attributes(pv)
      end
    rescue Net::ReadTimeout
      raise Pv4Timeout, "failed to load page view history due to service timeout"
    end

    def for_user(user, oldest: nil, newest: nil)
      bookmarker = Bookmarker.new(self)
      BookmarkedCollection.build(bookmarker) do |pager|
        bookmark = pager.current_bookmark
        if bookmark
          end_time, last_page_view_id = bookmark
          newest = Time.zone.parse(end_time)
        end
        pager.replace(fetch(user,
                            start_time: oldest,
                            end_time: newest,
                            last_page_view_id:,
                            limit: pager.per_page))
        pager.has_more! if pager.size >= pager.per_page
        pager
      end

View on GitHub (pinned to 1c9f0bb801)