Skip to content

Errors reference

Every error string authn produces, what causes it, and what to change. Strings are quoted as the package emits them; %q-style placeholders show where a value you supplied is interpolated.

Errors fall into two groups that you should treat completely differently:

  • Construction errors are configuration problems. They surface once, at NewAPIKeyVerifier / NewJWTVerifier time, and should stop your process starting.
  • Verification errors all wrap ErrUnauthenticated and mean the same thing on the wire: a generic 401. The detail is for your log.

Construction errors — API key

Error Cause Fix
authn: API-key verifier requires at least one key (fail-closed) NewAPIKeyVerifier() called with no entries Supply at least one KeyEntry. An empty set is refused because it would mean "accept anything".
authn: API-key entry 1 has an empty key a KeyEntry with Key: ""; the number is the zero-based index Populate the key, or drop the entry. A missing environment variable is the usual cause.

Construction errors — JWT

Error Cause Fix
authn: JWT verifier requires a JWKS URL (set JWKSURL or WithOIDCDiscovery) neither JWKSURL nor WithOIDCDiscovery supplied Set one of them.
authn: JWT verifier requires an Issuer Issuer empty and discovery not used Set JWTConfig.Issuer to the exact iss your tokens carry.
authn: algorithm "none" is never allowed AllowedAlgorithms contains none in any letter case Remove it. There is no override.
authn: HMAC algorithm "HS256" is not allowed with a JWKS (alg-confusion defence) AllowedAlgorithms contains a value starting HS in any letter case; the message echoes the value you supplied Remove it. A JWKS publishes public keys, and accepting HMAC would let an attacker use the public key as the shared secret.
authn: parse JWKS URL: parse "://bad": missing protocol scheme JWKSURL is not a URL Correct the URL.
authn: JWKS URL must be HTTPS, got "http" JWKSURL uses a scheme other than https Use HTTPS. There is no exemption for localhost or a test server.
authn: initial JWKS fetch: … the priming fetch failed; the wrapped cause follows See the JWKS fetch errors below.

Construction errors — OIDC discovery

All of these come from WithOIDCDiscovery.

Error Cause Fix
authn: parse OIDC issuer URL: … the issuer URL is not a URL Correct it.
authn: OIDC issuer URL must be HTTPS, got "http" the issuer URL uses another scheme Use HTTPS.
authn: build OIDC discovery request: … the derived discovery URL could not be turned into a request Check the issuer URL for stray characters.
authn: fetch OIDC discovery document: … the request failed — DNS, TLS, connection, or the 10-second deadline Check reachability and TLS trust from the running process, not from your laptop.
authn: OIDC discovery returned 404 the issuer served a non-200 for /.well-known/openid-configuration Confirm the issuer URL is the OIDC issuer, not the login page or the API base.
authn: read OIDC discovery document: … the body could not be read Usually a connection dropped mid-response.
authn: parse OIDC discovery document: … the body is not valid JSON, or exceeded 1 MiB and was truncated Fetch the URL yourself and look at what comes back — an HTML error page is the common cause.
authn: OIDC document issuer "https://issuer.example.com" does not match "https://issuer.example.com/" the document's issuer differs from the string you passed, byte for byte Pass the issuer exactly as the document advertises it. A trailing slash is the usual culprit — see A trailing slash on the issuer URL breaks discovery.

JWKS fetch errors

These surface at construction (wrapped in authn: initial JWKS fetch:) and again on any later refresh. On a refresh they are only returned to a caller when there is no usable cached key for the requested kid — otherwise the cached key is used and the failure is invisible.

Error Cause
authn: build JWKS request: … the JWKS URL could not be turned into a request
authn: fetch JWKS: … DNS, TLS, connection failure, or the 10-second deadline
authn: JWKS endpoint returned 404 any non-200 status
authn: read JWKS body: … the body could not be read
authn: JWKS document exceeds 1048576 bytes the document is larger than 1 MiB
authn: parse JWKS document: … the body is not valid JSON
authn: JWKS document has 65 keys, max 64 the document publishes more than 64 keys

Errors from an individual key — an unsupported kty, an unknown EC curve, an out-of-range RSA exponent, bad base64 — are never returned. That key is skipped and the rest of the document is kept, which is what allows a JWKS containing a key type authn does not support to keep working. The cost is that a document with no usable keys at all looks like a successful fetch.

Verification errors

Every one of these wraps ErrUnauthenticated, so errors.Is(err, authn.ErrUnauthenticated) is true for all of them, and every one maps to the same generic 401 on the wire.

Error Cause
authn: unauthenticated an API key that matched no configured entry. There is no further detail by design.
token is malformed: token contains an invalid number of segments: authn: unauthenticated the credential is not a JWT — an empty string or arbitrary text produces this
token has invalid claims: token is missing required claim: exp claim is required: authn: unauthenticated the token has no exp claim; exp is mandatory
token has invalid claims: token is expired: authn: unauthenticated exp is in the past by more than Leeway
token has invalid claims: token is not valid yet: authn: unauthenticated nbf is in the future by more than Leeway
token has invalid claims: token is missing required claim: iss claim is required: authn: unauthenticated the token has no iss claim
token has invalid claims: token has invalid issuer: authn: unauthenticated iss does not equal JWTConfig.Issuer
token is unverifiable: error while executing keyfunc: no JWKS key for kid "…": authn: unauthenticated: authn: unauthenticated (the quoted value is the token's kid, or "" if it has no kid header) no key in the cached JWKS matches the token's kid. The sentinel appears twice because the key lookup and the verify call each wrap it.
token signature is invalid: crypto/rsa: verification error: authn: unauthenticated the signature does not verify against the key found for that kid
token signature is invalid: signing method RS256 is invalid: authn: unauthenticated the token's alg is not in AllowedAlgorithms. Also produced by a case mismatch — listing "rs256" rejects RS256 tokens.
audience not accepted: authn: unauthenticated Audiences is set and none of the token's aud values matches; also produced when the token has no aud claim, or when aud is neither a string nor an array of strings
unexpected claims type: authn: unauthenticated the parsed claims were not jwt.MapClaims; not reachable through the public API
no verified client certificate: authn: unauthenticated VerifyCert received an empty chain set, or a first chain with no certificates — meaning the TLS stack verified no client certificate
client certificate has no usable subject: authn: unauthenticated the leaf has no Common Name, no DNS SAN and no URI SAN — or your WithCertSubject function returned ""

Distinguishing a bad credential from a broken dependency

Both come back from Verify as errors, but only rejections wrap the sentinel:

switch {
case err == nil:
    // authenticated
case errors.Is(err, authn.ErrUnauthenticated):
    // a rejected credential — expected traffic, log at info or debug
default:
    // operational: the JWKS could not be fetched and no cached key was usable
    // — log at error and alert on it
}

The second branch is the one that fills logs during an attack; the third is the one that means your issuer is unreachable. Both still return 401 to the caller.