instructure/canvas-lms · error · Pv4BadRequest

invalid request

Error message

invalid request

What it means

Pv4Client#fetch calls the PV4 page-views HTTP API for a user and maps response codes to typed errors; an HTTP 400 response raises Pv4BadRequest with message "invalid request". This means the PV4 service rejected the request as malformed — bad query parameters, an invalid user id format, or an unsupported parameter combination.

Solutions

  1. Inspect the outgoing URL and query params (log @uri before the request) and fix invalid parameter names/values or formats.
  2. Validate the user's global_id is present and correctly formatted before calling for_user.
  3. Sanitize time-range params (start_time/end_time) — ensure they are valid, ordered datetimes or omitted.
  4. Rescue Pv4BadRequest in the caller and degrade gracefully (e.g. return empty page views) since PV4 may not have data for the user.

Example fix

// before
Pv4Client.for_user(user, start_time: params['start_time']) # raw unvalidated string
// after
start = Time.zone.parse(params['start_time'].to_s)
Pv4Client.for_user(user, start ? { start_time: start.utc.iso8601 } : {})
Defensive patterns

Strategy: fallback

Validate before calling

raise ArgumentError, 'user required' unless user&.global_id
filters.each { |k, v| raise ArgumentError, "bad #{k}" if %i[start_time end_time].include?(k) && v && !v.is_a?(Time) }

Try / catch

begin
  Pv4Client.for_user(user, filters)
rescue Pv4BadRequest => e
  Rails.logger.warn("PV4 bad request for #{user.global_id}: #{e.message}")
  []
end

Prevention

When it happens

Trigger: Calling Pv4Client.for_user (which calls fetch) when the built request to users/{global_id}/page_views?{params} is rejected with 400 — e.g. invalid start_time/end_time params, unknown query parameter names, or a malformed user global id in the URL.

Common situations: Passing improperly formatted/nil time filters that serialize into garbage query strings; calling PV4 before the user's data exists in the new page-views store; proxy/gateway returning 400 for oversized or malformed requests.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


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

Appendix: source

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

              end_time: Time.now.utc,
              last_page_view_id: nil,
              limit: nil)
      end_time ||= Time.now.utc
      start_time ||= Time.at(0).utc

      params = "start_time=#{start_time.utc.iso8601(PRECISION)}"
      params << "&end_time=#{end_time.utc.iso8601(PRECISION)}"
      params << "&#{cached_root_account_uuids_for(user:)}"
      params << "&last_page_view_id=#{last_page_view_id}" if last_page_view_id
      params << "&limit=#{limit}" if limit
      response = CanvasHttp.get(
        @uri.merge("users/#{user.global_id}/page_views?#{params}").to_s,
        request_headers
      )

      case response.code.to_i
      when 400
        raise Pv4BadRequest, "invalid request"
      when 401
        raise Pv4Unauthorized, "unauthorized request"
      when 404
        raise Pv4NotFound, "resource not found"
      when 429
        raise Pv4TooManyRequests, "rate limit exceeded"
      end

      json =
        begin
          response.body.empty? ? {} : JSON.parse(response.body)
        rescue JSON::ParserError
          {}
        end
      raise Pv4EmptyResponse, "the response is empty or does not contain expected keys" unless json["page_views"]

      json["page_views"].map! do |pv|
        pv["session_id"] = pv.delete("sessionid")

View on GitHub (pinned to 1c9f0bb801)