Gradle "Failed to publish ... 401 Unauthorized" - Fix Publishing Auth in CI
Gradle authenticated to the publish target and was rejected with a 401. The publishing block sent no credentials or the wrong ones - the repository credentials {} is missing, or the CI secret holding the token is absent or stale.
What this error means
A publish/publishAllPublicationsTo...Repository task fails with Could not PUT '<url>'. Received status code 401 from server: Unauthorized. Resolution from the same repo works; only the upload fails.
> Task :app:publishMavenPublicationToReleasesRepository FAILED
> Failed to publish publication 'maven' to repository 'releases'
> Could not PUT 'https://nexus.example.com/.../app-1.0.0.jar'.
Received status code 401 from server: UnauthorizedCommon causes
No credentials on the publish repository
The maven { url = ... } repository in the publishing block has no credentials {} (or empty values), so Gradle uploads unauthenticated and the server returns 401.
Wrong or missing CI secret
The username/token is read from environment variables or Gradle properties that are not set in CI, or the token has expired, so authentication fails.
How to fix it
Add credentials from CI secrets to the publish repo
Wire the username/token into the publishing repository, sourced from CI environment variables.
publishing {
repositories {
maven {
name = "releases"
url = uri("https://nexus.example.com/repository/maven-releases/")
credentials {
username = System.getenv("REPO_USER")
password = System.getenv("REPO_TOKEN")
}
}
}
}Confirm the secret is injected into the job
Make sure the CI step actually exposes the token to the Gradle process.
- run: ./gradlew publish
env:
REPO_USER: ${{ secrets.REPO_USER }}
REPO_TOKEN: ${{ secrets.REPO_TOKEN }}How to prevent it
- Always declare
credentials {}on publish repositories, sourced from CI secrets. - Use a write/deploy-scoped token for publishing, separate from read tokens.
- Rotate publish tokens and confirm they are injected into the job env.