instructure/canvas-lms · error · ArgumentError
requestor_user is required for JWT generation
Error message
requestor_user is required for JWT generation
What it means
PageViews::ServiceBase#generate_jwt_token raises ArgumentError when @requestor_user is nil. Every service request signs itself with a CanvasSecurity::ServicesJwt issued for the requesting user, and that JWT is built in request_headers, so constructing a PageViews service (e.g. FetchResultService, ServiceBase subclass) without requestor_user: makes authenticated calls impossible.
Solutions
- Always pass requestor_user: when constructing the service, e.g. PageViews::FetchResultService.new(config, requestor_user: current_user)
- In controllers, guard that current_user is present (authenticate before reaching the service) or render 401 early
- In background jobs, load and pass the user object that originally initiated the page-views query
- Add an explicit check at the call site (raise early in initialize) if you want the failure at construction time rather than request time
Example fix
// before service = PageViews::FetchResultService.new(config) result = service.call(query_id) # ArgumentError: requestor_user is required // after raise "page_views requestor missing" unless current_user service = PageViews::FetchResultService.new(config, requestor_user: current_user) result = service.call(query_id)
Defensive patterns
Strategy: validation
Validate before calling
raise "requestor_user required for page_views" unless defined?(current_user) && current_user service = PageViews::FetchResultService.new(config, requestor_user: current_user)
Try / catch
begin
result = service.call(query_id)
rescue ArgumentError => e
raise unless e.message.include?("requestor_user")
render json: { error: "authentication_required" }, status: :unauthorized
end Prevention
- Always pass requestor_user: explicitly at every construction site; avoid relying on defaults
- Authenticate users before controller code reaches the service layer
- In jobs, persist and reload the initiating user instead of passing nil
- Grep for `PageViews::.*Service.new(` in code review to check requestor_user is supplied
When it happens
Trigger: Instantiating any PageViews::ServiceBase subclass (FetchResultService, query/polling services) without the requestor_user: keyword argument, then invoking a method that builds request_headers (call, poll, etc.). initialize accepts requestor_user: nil silently; the error only fires when a request is actually made.
Common situations: A controller passes current_user but the user is nil for an unauthenticated/API-token request path; a background job calls the service with only configuration and forgets the user; refactoring renamed the keyword argument (was positional or differently named) so the value silently falls back to the nil default.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- Attachment verifier token expired: #
- Attachment verifier token id mismatch. token id: #
- Cannot generate a services JWT without a 'sub' entry
- masquerading user not found
- refresh window exceeded
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/74744b1e1452fd5e.
Report an issue: GitHub.
Appendix: source
Thrown at app/services/page_views/service_base.rb:39
class ServiceBase
def initialize(configuration, requestor_user: nil)
@configuration = configuration
@requestor_user = requestor_user
end
protected
attr_reader :configuration
def request_headers
request_id = RequestContext::Generator.request_id
jwt_token = generate_jwt_token
{ "Authorization" => "Bearer #{jwt_token}",
"X-Request-Context-Id" => request_id }
end
def generate_jwt_token
raise ArgumentError, "requestor_user is required for JWT generation" unless @requestor_user
CanvasSecurity::ServicesJwt.for_user(
HostUrl.default_host,
@requestor_user,
encrypt: false,
base64: false
)
end
def get_with_clean_redirect(uri, headers, &)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == "https"
http.ssl_timeout = http.open_timeout = CanvasHttp::OPEN_TIMEOUT
http.read_timeout = CanvasHttp::READ_TIMEOUT
http.write_timeout = CanvasHttp::WRITE_TIMEOUT
http.max_retries = 0
response = http.request(Net::HTTP::Get.new(uri.request_uri, headers))
View on GitHub (pinned to 1c9f0bb801)