gitlabhq/gitlabhq · error · KeysFinder::InvalidFingerprint

Failed to return the key

Error message

Failed to return the key

What it means

KeysFinder#by_fingerprint raises InvalidFingerprint (rendered by the keys API as HTTP 400 'Failed to return the key', lib/api/keys.rb) when the fingerprint parameter fails validation. A SHA256 fingerprint (recognized by its sha256:/SHA256: prefix) must Base64-decode to exactly 32 bytes; an MD5 fingerprint must match 16 colon-separated hex pairs - and MD5 is rejected outright when the instance runs in FIPS mode.

Source

Thrown at app/finders/keys_finder.rb:78

    keys.created_after(params[:created_after])
  end

  def by_expires_before(keys)
    return keys unless params[:expires_before]

    keys.expires_before(params[:expires_before])
  end

  def by_expires_after(keys)
    return keys unless params[:expires_after]

    keys.expires_after(params[:expires_after])
  end

  def by_fingerprint(keys)
    return keys unless params[:fingerprint].present?
    raise InvalidFingerprint unless valid_fingerprint_param?

    keys.find_by(fingerprint_query) # rubocop:disable CodeReuse/ActiveRecord -- find_by needed for fingerprint lookup
  end

  def valid_fingerprint_param?
    return Base64.decode64(fingerprint).length == 32 if fingerprint_type == "sha256"

    return false if Gitlab::FIPS.enabled?

    fingerprint =~ /^(\h{2}:){15}\h{2}/
  end

  def fingerprint_query
    fingerprint_attribute = FINGERPRINT_ATTRIBUTES[fingerprint_type]

    Key.arel_table[fingerprint_attribute].eq(fingerprint)
  end

View on GitHub (pinned to 55ee20384a)

Solutions

  1. Use the SHA256 form: the literal prefix sha256: followed by the base64 digest that decodes to 32 bytes - exactly what `ssh-keygen -lf key.pub` prints.
  2. If you must use MD5, ensure the format is exactly 16 lowercase hex pairs separated by colons (aa:bb:...), but prefer migrating to SHA256.
  3. On FIPS instances, switch entirely to SHA256 fingerprints - MD5 lookups cannot work there.
  4. Copy the fingerprint from `GET /keys` output or the user's profile page rather than re-typing it.

Example fix

# before: hex-encoded sha256 digest is not valid
GET /api/v4/keys?fingerprint=SHA256:1f2e3d4c5b6a...64-hex-chars
# => 400 Failed to return the key

# after: base64 digest as printed by ssh-keygen -lf
GET /api/v4/keys?fingerprint=SHA256:H8xP4pCT6mQ0zqF3nJ9yQcVeEr5T5Y+1a2B3c4D5e6F
Defensive patterns

Strategy: validation

Validate before calling

def fingerprint_ok?(fp)
  if fp.match?(/sha256:/i)
    require 'base64'
    Base64.decode64(fp.sub(/sha256:/i, '')).bytesize == 32
  else
    !Gitlab::FIPS.enabled? && fp.match?(/^([0-9a-f]{2}:){15}[0-9a-f]{2}/)
  end
end

render_api_error!('bad fingerprint', 400) unless fingerprint_ok?(params[:fingerprint])

Type guard

def valid_sha256_fingerprint?(fp)
  fp.to_s.start_with?('sha256:', 'SHA256:') &&
    Base64.decode64(fp.sub(/(sha256:)|(SHA256:)/, '')).bytesize == 32
end

Try / catch

begin
  key = KeysFinder.new(fingerprint: fp).execute
rescue KeysFinder::InvalidFingerprint
  render_api_error!('Failed to return the key', 400) # terminal: fix the fingerprint format, do not retry
end

Prevention

When it happens

Trigger: GET /api/v4/keys?fingerprint=<malformed string>; supplying a SHA256 digest in hex (64 hex chars) instead of base64; sending an MD5 (colon-delimited) fingerprint to a FIPS-enabled GitLab; whitespace or truncation from copy-pasting the fingerprint.

Common situations: Tools that print fingerprints in hex; older scripts written before SHA256 fingerprints became the default; FIPS-compliant self-managed installations where MD5 lookups silently stopped working; UIs that strip colons from MD5 strings.

Related errors


AI-assisted analysis of gitlabhq/gitlabhq@55ee20384a (2026-08-21). Data as JSON: /api/errors/43c0b9d56fa2fa43. Report an issue: GitHub.