instructure/canvas-lms · error · ArgumentError
Must have a domain and a user to build a JWT
Error message
Must have a domain and a user to build a JWT
What it means
ServicesJwt.for_user builds a services JWT whose payload includes the domain and the user's global_id/uuid. Both a domain and a user object are mandatory; if either is blank/nil the JWT would be unusable, so ArgumentError is raised before any signing work.
Solutions
- Guard that domain is present and user is not nil before calling for_user
- Load/return the user first (authenticate! or find) and bail out when nil
- Derive the domain explicitly (e.g. from the account/root account) instead of a possibly-empty request.host
Example fix
// before
jwt = CanvasSecurity::ServicesJwt.for_user(request.host, @current_user)
// after
if request.host.present? && @current_user
jwt = CanvasSecurity::ServicesJwt.for_user(request.host, @current_user)
else
return render json: { error: 'unauthorized' }, status: :unauthorized
end Defensive patterns
Strategy: validation
Validate before calling
raise ArgumentError, 'need domain and user' if domain.blank? || user.nil?
Type guard
def jwt_buildable?(domain, user) = domain.present? && !user.nil?
Prevention
- Authenticate the user before minting service JWTs
- Pass an explicit domain, not request.host, in console/job contexts
- Guard controllers with authenticate! before token generation
When it happens
Trigger: Calling CanvasSecurity::ServicesJwt.for_user(nil_or_blank_string, user) or for_user(domain, nil), e.g. when the current_user is nil (unauthenticated request) or the request host is empty.
Common situations: Background jobs without a loaded user object; controllers hitting the method when current_user is nil; domain derived from request.host being empty in console/test contexts.
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
- all elements must be User objects
- Cannot decode nil token string
- Cannot generate a symmetric, non-encrypted JWT
- Date must be in YYYY-MM-DD format
- Date must be in YYYY-MM-DD format
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/28415f873fc2ff6c.
Report an issue: GitHub.
Appendix: source
Thrown at gems/canvas_security/lib/canvas_security/services_jwt.rb:105
)
elsif symmetric
CanvasSecurity.create_encrypted_jwt(payload, signing_secret, encryption_secret)
else
CanvasSecurity.create_encrypted_jwt(
payload,
CanvasSecurity::ServicesJwt::KeyStorage.present_key,
encryption_secret,
:autodetect
)
end
return crypted_token unless base64
CanvasSecurity.base64_encode(crypted_token)
end
def self.for_user(domain, user, real_user: nil, workflows: nil, context: nil, symmetric: false, encrypt: true, audience: nil, root_account_uuid: nil, base64: true)
if domain.blank? || user.nil?
raise ArgumentError, "Must have a domain and a user to build a JWT"
end
payload = {
sub: user.global_id,
user_uuid: user.uuid,
domain:
}
payload[:masq_sub] = real_user.global_id if real_user
if workflows.present?
payload[:workflows] = workflows
state = CanvasSecurity::JWTWorkflow.state_for(workflows, context, user)
payload[:workflow_state] = state unless state.empty?
end
if context
payload[:context_type] = context.class.name
payload[:context_id] = context.id.to_s
end
if audienceView on GitHub (pinned to 1c9f0bb801)