SonarSource/sonarqube · error · UnauthorizedException

User is not authenticated

Error message

User is not authenticated

What it means

ThreadLocalUserSession.get() is the accessor for the request-scoped UserSession stored in a ThreadLocal. When get() is called before any session has been bound to the current thread (DELEGATE.get() returns null), it throws UnauthorizedException('User is not authenticated'). The server uses this to signal that the current request carries no valid user session.

Source

Thrown at server/sonar-webserver-auth/src/main/java/org/sonar/server/user/ThreadLocalUserSession.java:45

import org.sonar.db.entity.EntityDto;
import org.sonar.db.permission.GlobalPermission;
import org.sonar.db.permission.ProjectPermission;
import org.sonar.db.user.GroupDto;
import org.sonar.server.exceptions.UnauthorizedException;

/**
 * Part of the current HTTP session
 */
public class ThreadLocalUserSession implements UserSession {

  private static final ThreadLocal<UserSession> DELEGATE = new ThreadLocal<>();

  public UserSession get() {
    UserSession session = DELEGATE.get();
    if (session != null) {
      return session;
    }
    throw new UnauthorizedException("User is not authenticated");
  }

  public void set(UserSession session) {
    DELEGATE.set(session);
  }

  public void unload() {
    DELEGATE.remove();
  }

  public boolean hasSession() {
    return DELEGATE.get() != null;
  }

  @Override
  @CheckForNull
  public Long getLastSonarlintConnectionDate() {
    return get().getLastSonarlintConnectionDate();

View on GitHub (pinned to 184c821202)

Solutions

  1. Authenticate the request: pass a valid user token ('Authorization: Bearer <token>') or basic credentials to the API call
  2. Check authentication requirements of the endpoint and use an endpoint that permits anonymous access, or enable anonymous access in server settings if intended
  3. Verify the token was not revoked/expired and belongs to an active user (Admin > Security > Users)
  4. In server-side code, ensure UserSession is set via ThreadLocalUserSession.set() before calling accessors

Example fix

// before
curl http://sonar.example.org/api/users/current
// after
curl -u "mytoken:" http://sonar.example.org/api/users/current
Defensive patterns

Strategy: try-catch

Validate before calling

// client: prefer a cheap auth probe before dependent calls
const res = await fetch(`${baseUrl}/api/authentication/validate`, { headers: { Authorization: `Bearer ${token}` } });
if (!res.ok || !(await res.json()).valid) throw new Error('Not authenticated: supply a valid SonarQube user token');

Type guard

// Java server-side
guard: if (sessionRef.get() == null /* or catch UnauthorizedException */) redirect/401 before calling getLogin/getUuid

Try / catch

try {
  String login = userSession.getLogin();
} catch (UnauthorizedException e) {
  // respond 401 / prompt for token
  return Response.status(401).build();
}

Prevention

When it happens

Trigger: Calling any accessor (getLogin, getUuid, getName, getGroups, getIdentityProvider, getLastSonarlintConnectionDate) on ThreadLocalUserSession for a request with no authenticated session, e.g. anonymous request to an endpoint requiring authentication, or a missing/invalid credentials header.

Common situations: Scripts or clients calling the SonarQube web API without a token; an expired/revoked token; provisioning or background threads that never set a session before querying user info.

Understand the failure class

Related errors


AI-assisted analysis of SonarSource/sonarqube@184c821202 (2026-09-09). Data as JSON: /api/errors/b6d3e8d83c9bf6d9. Report an issue: GitHub.