instructure/canvas-lms · error · ArgumentError
Cannot generate a symmetric, non-encrypted JWT
Error message
Cannot generate a symmetric, non-encrypted JWT
What it means
Symmetric (shared-secret) services JWTs are deprecated in Canvas, and a symmetric unencrypted JWT would be trivially forgeable since a symmetric signing key plus no encryption offers no security. The generate method therefore explicitly forbids the combination symmetric: true with encrypt: false by raising ArgumentError.
Solutions
- Stop passing symmetric: true and use the default asymmetric/encrypted token path
- If you must stay symmetric, keep encrypt: true (the default)
- If you want unencrypted tokens, drop symmetric: true and use the asymmetric flow
Example fix
// before ServicesJwt.generate(payload, symmetric: true, encrypt: false) // after ServicesJwt.generate(payload) # or explicitly: ServicesJwt.generate(payload, symmetric: true, encrypt: true)
Defensive patterns
Strategy: validation
Validate before calling
raise ArgumentError if symmetric && !encrypt
Type guard
def valid_generate_opts?(symmetric:, encrypt:) = !symmetric || encrypt
Prevention
- Use the default generate options unless migration requires otherwise
- Treat symmetric tokens as deprecated; plan migration to asymmetric
- Grep codebase for symmetric: true usages
When it happens
Trigger: Calling CanvasSecurity::ServicesJwt.generate(payload, symmetric: true, encrypt: false) — directly or via defaults set in a wrapper.
Common situations: Migrating old code that used symmetric tokens and flipping encrypt off for debugging; porting legacy Canvas code that predates the deprecation.
Understand the failure class
Background: "is deprecated and will be removed" — deprecation warnings for old API names, keywords, and options, and how to migrate before the removal release — this error's family across 29 libraries.
Related errors
- Attachment verifier token id mismatch. token id: #
- Attachment verifier token invalid: #
- can't build pseudonym_credentials except on just-generated…
- Cannot decode nil token string
- DONT USE THIS, use .short_name instead
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/48feff4588905b19.
Report an issue: GitHub.
Appendix: source
Thrown at gems/canvas_security/lib/canvas_security/services_jwt.rb:78
def id
original_token[:jti]
end
def user_global_id
original_token[:sub]
end
def masquerading_user_global_id
original_token[:masq_sub]
end
def expires_at
original_token[:exp]
end
# Symmetric services JWTs are now deprecated
def self.generate(payload_data, base64: true, symmetric: false, encrypt: true)
raise ArgumentError, "Cannot generate a symmetric, non-encrypted JWT" if symmetric && !encrypt
payload = create_payload(payload_data)
crypted_token = if !encrypt
CanvasSecurity.create_jwt(
payload,
nil,
CanvasSecurity::ServicesJwt::KeyStorage.present_key,
:autodetect
)
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
)View on GitHub (pinned to 1c9f0bb801)