instructure/canvas-lms · warning · ImpossibleCredentialsError
pseudonym cannot have a unique_id of length #
Error message
pseudonym cannot have a unique_id of length #{credentials[:unique_id].length} What it means
Pseudonym raises ImpossibleCredentialsError during login credential processing when the supplied unique_id (login/email) exceeds 255 characters. Such credentials can never succeed because the column limit makes them impossible to match, and past behavior produced noisy errors instead of a clean failed login.
Solutions
- Return a normal failed-login response instead of a 500 — the raise exists so callers rescue ImpossibleCredentialsError and treat it as bad credentials
- Add client-side/server-side length validation (<= 255) on the login field before authentication
- Investigate the client or integration producing >255-char unique_ids
- Sanitize/trim login input at the edge (proxy, SSO adapter) before it reaches Pseudonym
Example fix
// before login(params[:pseudonym_session][:unique_id]) // after uid = params[:pseudonym_session][:unique_id] if uid && uid.length > 255 return failed_login end login(uid)
Defensive patterns
Strategy: validation
Validate before calling
uid = params.dig(:pseudonym_session, :unique_id) return failed_login if uid.blank? || uid.length > 255
Type guard
def valid_unique_id?(uid) uid.is_a?(String) && uid.length.between?(1, 255) end
Try / catch
begin authenticate(pseudonym_session) rescue ImpossibleCredentialsError failed_login end
Prevention
- Enforce a 255-char max on login/email inputs client- and server-side
- Scrub login forms against pasted blobs (JWTs, URLs)
- Check SSO/LDAP adapters for identifier truncation rules
- Monitor for oversized unique_id attempts as bot traffic
When it happens
Trigger: A login attempt where credentials[:unique_id].length > 255 — e.g. a malicious or malformed POST to the login endpoint, a client bug concatenating values into the login field, or pasted oversized data into the login form.
Common situations: Bots or scanners submitting garbage to /login; SSO/LDAP misconfiguration sending oversized identifiers; user pasting a long string (e.g. a JWT or URL) into the email field; automated tests with invalid fixtures.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- No login_id given for user #
- # is too long, max length is # characters
- and cannot be used together
- and cannot be used together
- A course did not pass validation
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/d757cfb4eda3e6c1.
Report an issue: GitHub.
Appendix: source
Thrown at app/models/pseudonym.rb:771
scope :active_only, -> { where(workflow_state: "active") }
scope :deleted, -> { where(workflow_state: "deleted") }
def self.serialization_excludes
%i[crypted_password password_salt reset_password_token persistence_token single_access_token perishable_token sis_ssha]
end
def self.associated_shards(_unique_id_or_sis_user_id)
[Shard.default]
end
def self.find_all_by_arbitrary_credentials(credentials, account_ids)
return [] if credentials[:unique_id].blank? ||
credentials[:password].blank?
if credentials[:unique_id].length > 255
# this sometimes happens by mistake, and produces noisy errors.
# we can handle this error explicitly when it arrives and just return
# a failed login instead of an error.
raise ImpossibleCredentialsError, "pseudonym cannot have a unique_id of length #{credentials[:unique_id].length}"
end
error = nil
begin
associated_shards = associated_shards(credentials[:unique_id])
rescue => e
# global lookups is just an optimization anyway; log an error, but continue
# by searching all accounts the slow way
Canvas::Errors.capture(e)
end
pseudonyms = Shard.partition_by_shard(account_ids) do |shard_account_ids|
next if GlobalLookups.enabled? && associated_shards && !associated_shards.include?(Shard.current)
active_only
.by_unique_id(credentials[:unique_id])
.where(account_id: shard_account_ids)
.preload(:user)
.select do |p|View on GitHub (pinned to 1c9f0bb801)