# Diagnose SonarQube scanner 401 / "Not authorized" authentication failures

Classify a SonarQube scanner authentication failure from the log before acting: wrong server URL (scanners default to SonarQube Cloud when sonar.host.url is unset), invalid token, missing Execute Analysis / Create Projects permissions, or deprecated sonar.login usage — each with its distinct fix.

Exact reference: {"kind":"skill_version","skill_id":"skl_PMrDcX7u1S1ZOACBfRN1nQ","version_id":"skv__nY07VJ2aYZT2xL1xBdXcQ"}

Applicability: [{"constraint":">=10.0","technology":"SonarQube Server","version_scheme":"semver"},{"constraint":"all plans","technology":"SonarQube Cloud","version_scheme":"unknown"},{"constraint":">=5.0","technology":"SonarScanner CLI / build scanners","version_scheme":"semver"}]

# Diagnose SonarQube scanner 401 / "Not authorized" authentication failures

Use this when a SonarQube analysis fails during bootstrap with `401 Unauthorized`,
`Not authorized`, or "not authorized to analyze this project". Scanner auth failures
look identical whether the cause is a bad token, a wrong server URL, or missing
project permissions — read the log first and classify before regenerating anything.

## 1. Find out which server the scanner is actually talking to

The single most common cause (per SonarSource staff on the community forum) is that
recent scanner versions default to SonarQube Cloud when no `sonar.host.url` is
provided. Your perfectly valid self-hosted token then 401s against the wrong server.

Look at the log before the 401. You will see one of these:

```
[INFO] Bootstrapper: Server URL: https://sonarcloud.io
```

or a failing request to an unexpected host:

```
[ERROR] Failed to execute goal ... on project myapp: Error status returned by url
[https://api.sonarcloud.io/analysis/jres?os=windows&arch=amd64]: 401
{
"Message": "User is not authorized to access this resource with an explicit deny"
}
```

**If the URL is Cloud (sonarcloud.io / api.sonarcloud.io) but your instance is
self-hosted, this is a URL problem, not a token problem.** Set the server URL
explicitly on every scanner invocation:

```
-Dsonar.host.url=http://YOUR_SONARQUBE_HOST:9000
```

(`sonar.host.url` accepts the `SONAR_HOST_URL` environment variable too, but a
command-line property always wins over the env var and over
`sonar-project.properties` — when the symptom says the scanner ignored your URL,
an env var or global config is the usual suspect. Check `$install_directory/conf/sonar-scanner.properties`
and any CI-level `SONAR_HOST_URL` for conflicts.)

## 2. Classify the 401 from the exact error text

- **`Not authorized. Please check the user token in the property 'sonar.token' or 'sonar.login' (deprecated).`**
  with a 401 on `api/settings/values.protobuf` — the token is missing, revoked,
  expired, or belongs to a different server. Generate a fresh token in the UI
  (My Account > Security, or Administration > Security > Users > Tokens for a
  service user). The token value is displayed exactly once at creation; there is
  no way to recover it later, only to revoke and regenerate. Then confirm the new
  token authenticates: a GET against the server's API with the token as the bearer
  credential must return 200, not 401.

- **`You're not authorized to analyze this project or the project doesn't exist`**
  — this message is deliberately ambiguous and usually means a *permission*
  problem, not a credential problem. The token is valid, but its user lacks
  **Execute Analysis** on that project. For the first analysis of a
  not-yet-provisioned project the user also needs the global **Create Projects**
  permission; otherwise provision the project in the UI first with the exact
  `sonar.projectKey` the scanner sends. Double-check the project key for typos —
  `my-app` vs `my_app` produces this exact error.

- **401 with `"explicit deny"`** — the user is blocked by a permission template or
  an explicit denial rather than by a missing grant. Same fix path as above, via
  the project's permissions page, not via a new token.

## 3. Use `sonar.token`, not `sonar.login`

`sonar.login` is deprecated; the scanners and the error message itself now point at
`sonar.token`. On SonarQube Server 10.x the token model also changed: there are
project analysis tokens and global analysis tokens in addition to user tokens. A
project analysis token only works for its own project — reusing it for a second
project key yields a 401 that looks like a bad credential but is really a scope
mismatch. When in doubt, generate a user token (My Account > Security) for CI and
check which project it is being used against.

Never commit the token to the repo. Keep it in the CI system's secret store and
let the scanner read it from there; the scanner resolves the token property from
the command line, the documented environment variable, or the CI secret binding —
pick one channel and use it consistently so a stale value in another channel
cannot shadow it.

## 4. Separate auth failures from transport failures

- If the scanner fails *before* any 401 with certificate or TLS errors, that is a
  trust problem, not an auth problem (one Jenkins thread fixed it with the scanner
  property `sonar.scanner.skipSystemTruststore=true` against a corporate CA).
  Do not regenerate tokens for a TLS error.
- If the token's "last used" timestamp in the UI never moves, the scanner is not
  actually sending it — you are looking at the wrong property name or the value
  is empty after variable expansion in CI. Print the property *name* being passed
  (never the value) in a debug step to confirm.

## 5. Checklist for the failing scan

1. Identify the server URL in the log. If it is not your server, set
   `-Dsonar.host.url` explicitly on the command line.
2. Match the error text: bare `Not authorized` (bad/missing token) vs
   "not authorized to analyze this project" (permissions/project key) vs
   explicit deny (permission template).
3. Confirm the token works with a direct authenticated API call; if the UI shows
   it was never used, the scanner is not receiving it.
4. Confirm the token's user has Execute Analysis on the project, or Create
   Projects globally for first-time provisioning.
5. Confirm you are passing `sonar.token` (not the deprecated `sonar.login`) and
   that exactly one channel supplies it — command line beats env beats properties
   file.


## Supporting basis and limitations

Built from SonarSource community threads (notably the Maven 401 thread where staff identify the default-to-Cloud host URL change, and the Jenkins withSonarQubeEnv Not-authorized thread quoting the exact 'sonar.token or sonar.login (deprecated)' error), plus the scanner's documented property precedence and the SonarQube 10.x token/permission model.

## Change and rationale

New skill: diagnose SonarQube scanner 401 / Not authorized authentication failures.

Scanner auth failures are among the most-asked SonarQube questions and they all look identical (401) whether the cause is a missing host URL, a bad token, or a permission gap — so agents default to regenerating tokens, which fixes only one of the four causes. This skill adds a classify-first procedure keyed on the exact error text and the server URL in the log.
