Authentication and authorization failures
Auth failures split into two questions the server asks in order: who are you? (authentication — fails as 401 or "invalid token") and are you allowed? (authorization — fails as 403 or "access denied"). Fixing the wrong one wastes the afternoon, so classify first.
401 family: the server doesn't believe you
Missing header, malformed header (Bearer prefix absent or doubled), an expired
token, a token signed with the wrong key, or a token for the wrong audience/issuer. JWTs
make this concrete — decode the payload (it is only base64, no secret needed) and read it:
echo "$TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null | jq '{exp: (.exp | todate), aud, iss, scope}'
Check exp against the current time, and aud/iss
against what the receiving service validates. Two non-obvious causes worth ruling out early:
clock skew (a machine minutes off makes valid tokens "expired" or "not yet valid"), and
rotated signing keys with a service still caching the old JWKS.
403 family: it believes you, and the answer is no
Credentials are valid but lack the specific permission: missing OAuth scope, IAM policy gap, role not granted, or resource-level rules (private repo, another tenant's object). The fix lives in the provider's permission model, not in your code — request the scope during authorization, grant the role, or use the identity that owns the resource. Note that some APIs return 404 instead of 403 for resources you can't see, to avoid leaking existence.
Failures that masquerade as bugs
API keys pasted with trailing newlines or wrapped in quotes; env vars set in your shell but not in the service's environment (CI, systemd, container); keys for the wrong environment (test vs live — Stripe-style prefixes make this checkable); OAuth refresh tokens silently revoked when a user changes their password. When a request works in curl but not in code, print the exact header your code sends — byte for byte — before suspecting the provider.
Handling it properly
On 401 with a refresh token: refresh once, retry once, and if that fails surface a re-authentication error — a refresh loop against a revoked grant is a rate-limit incident. On 403: never retry; log identity, resource, and required permission so the operator can fix the grant. And keep secrets out of logs — log the key's name or prefix, never the value.
Documented occurrences
3,801 analyzed errors across 606 libraries match this failure class. Each links to the thrown message, its source line, and documented fixes.
apereo/cas
- access_denied: Service [{}] requests authentication
- Access Denied for user [${username}] from IP Address [${request.getRemoteAddr()}]
- Adaptive authentication policy does not allow this request for [agent] and [geoLocation]
- An authentication pre-processor could not successfully process the authentication transaction
- Authenticated profile does not carry the UMA protection scope
- +114 more in cas
passbolt/passbolt_api
- An authentication token should be provided.
- An authentication token should be provided.
- An authentication token state is required.
- Conflicting authentication parameters, provide user_id/token only when the user is not already signed in.
- Could not update the authentication token data.
- +82 more in passbolt_api
BerriAI/litellm
- Access denied to directory '{directory_path}'. Check your BitBucket permissions for workspace '{self.workspace}' and repository '{self.repository}'.
- Access denied to directory '{directory_path}'. Check your GitLab permissions for project '{self.project}'.
- Access denied to file '{file_path}'. Check your BitBucket permissions for workspace '{self.workspace}' and repository '{self.repository}'.
- Access denied to file '{file_path}'. Check your GitLab permissions for project '{self.project}'.
- Access denied to managed resource.
- +56 more in litellm
spring-projects/spring-security
- Access Denied
- AclEntryAfterInvocationProvider.noPermission: Authentication {0} has NO permissions to the domain object {1}
- An Authentication object was not found in the SecurityContext
- Anonymous access to the login page doesn't appear to be enabled. This is almost certainly an error. Please check your configuration allows unauthenticated access to the configured login page. (Simulated access was rejected)
- Authenticated principal required to operate with ACLs
- +55 more in spring-security
apache/pulsar
- A v5 authentication plugin (${v5Authentication.getClass().getName}) cannot be serialized with the client configuration. Configure authentication with authPluginClassName + authParams instead of a pre-built plugin instance when the configuration has to cross a boundary.
- AUTH_REQUIRED: Authentication required
- Authentication already closed.
- Authentication has not completed
- Authentication method missing
- +54 more in pulsar
tailscale/tailscale
- access denied
- access denied
- access denied
- bugreport access denied
- conn25/state access denied
- +47 more in tailscale
quarkusio/quarkus
- An exception should have been thrown because authentication happened before Tenant was selected with the @Tenant annotation
- Annotations '<annotations>' can only be used when proactive authentication is disabled and either Quarkus REST, RESTEasy Classic or WebSockets Next extension is present
- Authentication has already been set
- Authentication has happened before the '@AuthenticationContext' annotation was matched with the HTTP request path '%s'. It can happen when the authentication is required by an HTTP Security Policy before the JAX-RS chain is run. In such cases, please set the 'quarkus.http.auth.permission."permissions".applies-to=JAXRS' to all HTTP Security Policies which secure the same REST endpoints as the ones annotated with the '@AuthenticationContext' annotation.
- Authentication mechanism must not be null or blank
- +38 more in quarkus
paperclipai/paperclip
- Access denied
- Agent authentication failed
- Agent authentication required
- Agent authentication required
- assigneeUserId=me requires board authentication
- +37 more in paperclip
apache/hadoop
- Access denied: dfs.http.policy is HTTPS_ONLY.
- "Access denied for user " + pc.getUser() + ". Superuser or owner of parent folder privilege is required"
- Access denied for user {}. Superuser privilege is required for operation {}
- Access Denied : {path}
- Access denied: User {} does not have permission to view job {}
- +35 more in hadoop
theonedev/onedev
- Access denied
- Authentication required
- Authentication required
- Authentication required
- Cannot delete primary email address of externally authenticated user
- +33 more in onedev
…and 596 more libraries — search for your exact message.
Other failure classes
- Connection failures: ECONNREFUSED, ECONNRESET, and friends
- DNS resolution errors: ENOTFOUND and getaddrinfo failures
- SSL/TLS and certificate errors
- Timeouts: ETIMEDOUT, deadlines, and hung requests
- HTTP status errors: handling 4xx and 5xx responses
- Parsing and encoding errors: unexpected token, malformed input