# 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.