Infrastructure worth keeping — and kept patched.

Gradle build cache authentication: 401 and status codes

Gradle does not fail the build when the cache refuses it, so a wrong password or a refused upload often shows up only as a status code. FosterStack Cache takes an optional password, and this page shows how to set it, how to give Gradle the right one, and what each status code means.

Our advice, not a test result

Turn on a password on the server

FSCACHE_USERNAME=gradle FSCACHE_PASSWORD=<a long secret> ./fscache

This is HTTP Basic Auth. The health and metrics paths stay open without a password, so monitoring still works. The status page does not. For a second, read-only login see let CI write, developers only read.

Tell Gradle the password

In settings.gradle.kts:

// FosterStack Cache — remote Gradle build cache.
// https://github.com/fosterstack/cache
buildCache {
    remote<HttpBuildCache> {
        url = uri("https://cache.example.com/")
        isPush = true
        credentials {
            username = "gradle"
            password = System.getenv("FSCACHE_PASSWORD")
        }
    }
}

Three ways to supply the password

In our runs all three gave :compileJava FROM-CACHE:

What each status code means

Gradle talks to the cache with plain web requests, and it does not fail the build when the cache says no. So the status code is often the only clue.

StatusWhen you get itWhat to do
200A stored entry was found and sent.Nothing. This is a cache hit.
201An upload was stored.Nothing.
404That key is not stored.Usually nothing. It is a cache miss. With a correct password it means the key is not stored.
401No password, or the wrong one.Fix the credentials block. See below.
403A write sent with the read-only login.Use the read-write login for machines that upload, or set the client not to upload.
413An upload bigger than the server's size limit for one entry, or bigger than the whole cache size cap.With no X-FSCache-Reject header, the entry is over the per-entry limit: raise FSCACHE_MAX_BODY_BYTES if it is meant to be cached. With the header set to entry-exceeds-cache-cap, the entry is bigger than FSCACHE_MAX_BYTES: see how the size limit works.
429Too many uploads in progress at once. The server allows 32 by default, and a refused upload stores nothing.The build carries on without that entry. If it keeps happening, see how many builds can use one cache server at once for the setting.

401: a wrong or missing password

Gradle treats a broken remote cache as non-fatal, so the build still succeeds, just without the cache. In our runs it printed this at the default log level, with a wrong password and with none:

Could not load entry d4f2de693b561fd2c5d60b17a9232d6f from remote build cache: Loading entry from 'http://127.0.0.1:18099/d4f2de693b561fd2c5d60b17a9232d6f' response status 401: Unauthorized

If you see that line, the password Gradle sent was wrong or missing. The usual causes are no credentials block, a wrong password, or a typo. Run with -i for more detail. To test the server directly:

curl -s -o /dev/null -w '%{http_code}\n' -u gradle:wrong-password https://cache.example.com/some-key

A 401 here means the credentials are wrong. A 404 means they are right and the key simply is not stored. More steps are in Gradle remote build cache not working.

403: the login worked but may not write

The server can have an optional read-only login. A client holding it can read (GET and HEAD return 200 as normal) but any write gets a 403. See can someone poison our shared build cache.

413: entry size

A single upload over the limit is refused with 413 and nothing is stored, so a later request for that key is a 404. The limit is FSCACHE_MAX_BODY_BYTES and defaults to 1 GiB per entry.

If the password seems ignored

./gradlew --stop is a quick way to rule the Gradle daemon out. In our test a changed password was picked up without a restart.

How we checked

A 0.2.1 server and Gradle 9.8.0 on macOS, started with a read-write login, a read-only login, and the body limit set to 1024 bytes. Your numbers for the limit will differ, since the default is 1 GiB. This list comes from these runs:

GET /healthz (no credentials)                                  200
GET /metrics (no credentials)                                  200
GET key, no credentials                                        401
GET key, wrong password                                        401
GET missing key, read-write login                              404
PUT 5 bytes, read-write login                                  201
GET stored key, read-write login                               200
GET stored key, read-only login                                200
HEAD stored key, read-only login                               200
PUT, no credentials                                            401
PUT, read-only login                                           403
PUT 2048 bytes (limit set to 1024), read-write login           413
GET the too-large key afterwards                               404
/statusz, no credentials                                       401

Second run, on a GitHub-hosted Linux machine with FosterStack Cache 0.2.2 and Gradle 9.8.0. The results:

The run and its log are public. Not tested there: the 1 GiB default for FSCACHE_MAX_BODY_BYTES, which needs an entry bigger than that.

Related: Gradle remote build cache not working, server sizing and monitoring.

Gradle build cache tutorial FosterStack home