:wave: Hi, is there a way to find the build that u...
# community-support
s
👋 Hi, is there a way to find the build that uploaded the Build Cache entry containing this metadata values ?
Copy code
buildCacheKey=b4c52b0ffac21f5823d8cb885f76413b
buildInvocationId=bueeaav7o5fazlizc4e6fajtwa
We are currently observing build entries being overwritten with empty content (only the METADATA file and 3 empty directories):
Copy code
cache-entry-b4c52b0ffac21f5823d8cb885f76413b
├── METADATA
├── tree-classpathSnapshotProperties.classpathSnapshotDir
├── tree-destinationDirectory
└── tree-taskBuildCacheableOutputDirectory%24kotlin_gradle_plugin_common

4 directories, 1 file
(the
%24
url-encoded name of the last directly looks suspicious as well)
v
Is the hostname, username, and creation time in the metadata file not sufficient to identify the build? If you have the daemon logs at hand (no ephemeral build agents or preserved somewhere), you could also use those together with the information from the metadata file to identify the build.
s
unfortunately no, it is running on CI with ephemeral machines. We have some clues thanks to the
creationTime
and
identity
, but we have multiple builds in parallel and the
creationTime
does not appear in the build scans to 100% confirm.
Copy code
#Generated origin information
#Tue Aug 04 12:18:59 CEST 2026
buildCacheKey=b4c52b0ffac21f5823d8cb885f76413b
buildInvocationId=bueeaav7o5fazlizc4e6fajtwa
creationTime=1785838739961
executionTime=0
gradleVersion=9.6.1
identity=\:features\:RealEstateLeadForm\:compileReleaseTestFixturesKotlin
type=org.gradle.api.internal.tasks.execution.TaskExecution
I'm currently crawling through all build scans with the API in the hope to find something
After looking up all builds, we have 2 parallel builds ~that uploads "sane" entries~: 115019 CEST
Copy code
Started after    10m 18.901s    
Duration    0.098s    
The task was not up-to-date because there was no task history available.    
Build cache result    Miss (local), Store (local)    
Cache key    b4c52b0ffac21f5823d8cb885f76413b    
Cache artifact    2.9 KiB / 11 entries    
Local miss    0.000s    
Pack    0.000s    
Local store    0.000s
115101 CEST
Copy code
Started after    9m 50.240s    
Duration    0.122s    
The task was not up-to-date because there was no task history available.    
Build cache result    Miss (local), Store (local)    
Cache key    b4c52b0ffac21f5823d8cb885f76413b    
Cache artifact    2.9 KiB / 11 entries    
Local miss    0.000s    
Pack    0.001s    
Local store    0.000s
and the next build at 120127 CEST
Copy code
Started after    17m 34.201s    
Duration    0.564s    
Avoidance savings    -0.560s (No origin Build Scan available)    
The task was not up-to-date because there was no task history available.    
Build cache result    Miss (local), Hit (remote), Store (local)    
Cache key    b4c52b0ffac21f5823d8cb885f76413b    
Cache artifact    638 B / 4 entries    
Local miss    0.000s    
Remote hit    0.559s at 1.1 KiB/s    
Unpack    0.001s    
Local store    0.000s
fetches the bad entry. They all have the same key, and in no other build scan I've found the "store" of this 4 entries build cache 🙃
Actually, i misread the first two scans. They mention
Miss (local), Store (local)
so they never stored the cache entry. Furthermore, they both have
The remote build cache was disabled during the build due to errors.
error message in their Performance/BuildCache page. Which could signify that the remote Build Cache was messing with these entries...
While it could be someone poisoning the build cache, I can't confirm this with the only information I've gathered from the metadata file and build scans. e.g. it could be a build cache stored without any corresponding build scan (--no-build-scan) and in this case how could I debug this further ? (we don't have specific authentication required to upload/override build cache entries, we simply are being an authenticated VPN). The other possibility is simply the Build Cache node randomly corrupting entries, but I guess it's impossible to confirm. I tried to find build cache references in a generated support bundle but did not find any match.
v
Which could signify that the remote Build Cache was messing with these entries
I don't think so. As far as I understand it, the remote build cache was already disabled before that task, which is why it didn't even search for a remote entry, otherwise it would have "hit (remote)", "miss (remote)", or "store (remote)". If you look through the output of those builds, you should earlier find some error regarding remote cache like it not being reachable which disables it for the rest of the build completely. Also, the former two builds do not match the timestamp of the cache entry creation. The third would match, but as it got a remote hit, it must have been come from some other build probably seconds before.
and in this case how could I debug this further ?
Well, if you neither have the build logs, nor build scans of the build in question, it will probably become quite hard. Unfortunately, the
Is the hostname, username, and creation time in the metadata file not sufficient to identify the build?
I mentioned was from an older version. A cache entry I looked at that was created with 7.6.4 had the hostName, userName, operatingSystem as part of the
METADATA
file, this would help you here maybe as you could at least see which user on which host created this cache entry. Unfortunately, those were removed in 8.7.0-RC1 with commit github.com/gradle/gradle/commit/5ee749f7acbf…. Imho, you should open a feature request to get them added back in, as for cases like this, it is extremely useful and important information. The next to last idea I have is looking at log files of whatever you use as remote build cache. Maybe you can see in some access log or similar from which IP address that cache entry was stored and that way find out something. And last idea, assuming you have build scans for all CI builds and as you said your remote build cache is world-writable, and assuming it was not an intentional poisoning, ask all your colleagues to grep their daemon logs to maybe find when, where, and why this entry was stored. (or make them send you their logs and grep over all of them)
(we don't have specific authentication required to upload/override build cache entries, we simply are being an authenticated VPN).
That is something I would never ever ever ever ever in my life do. For me, remote build-cache is and should always be read-only for each and everyone, except for CI. Only CI which should usually always run on good state and on defined situation remote cache entries are written and all others (including CI) benefits from those entries by being able to use them. By having a world-readable remote cache, anyone in that world (the VPN) can intentionally or unintentionally poisen the build cache. If you for example am a build engineer and develop a new task and did not properly implement the task yet, you already poisen the remote cache with bad entries that might be reused later. And you also waste a lot of remote cache space. Because ever changed character in the source files creates a new remote cache entry for the compilation task and every downstream task. State that will never be seen by any other computer and person, because that person will do 150 other code changes before knitting a commit and pushing it. Such a pushed commit, that is then seen and built by CI is the one that also other persons and the CI might see and for which they benefit from the remote build cache. All the intermediate ones are just a waste of space that noone at all will benefit from. Besides that you tremendously reduce the likelihood of intentional or unintentional cache poisoning. For example take this simple example:
Copy code
tasks.register("foo") {
    val out = layout.buildDirectory.file("foo.txt")
    outputs.file(out)
    outputs.cacheIf { true }
    doLast {
        if (findProperty("foo") == "bar") {
            out.get().asFile.writeText("foo")
        }
    }
}
Now if someone runs this task, without
foo
being set, it produces no output file. This state is persisted as cache entry. If you change now
foo
to
bar
, the file would be created, but it isn't. As the
foo
property value was not properly declared as task input, it is not part of its fingerprint and thus also not part of the cache key. The task will be up-to-date. If you now change the
"foo"
string to
"bar"
in the
writeText
call, the task implementation and thus its input changed and the task will run again, so if you run it now with
foo
set to
bar
the file is generated. If you now revert back the string to
"foo"
and run the task, no matter what
foo
is set to, the task will come
FROM-CACHE
and the file is removed as in that cached result the file was absent.
s
As far as I understand it, the remote build cache was already disabled before that task, which is why it didn't even search for a remote entry, otherwise it would have "hit (remote)", "miss (remote)", or "store (remote)".
If you look through the output of those builds, you should earlier find some error regarding remote cache like it not being reachable which disables it for the rest of the build completely.
yes, this happens right after our included build finishes compilation,
Could not store entry 59bab8fcd87d2e27211181ce2f4477ef in remote build cache: Read timed out
We've been trying to understand when these timeout happens (it happens frequently on our Gradle Build Cache node, and we are in contact with Develocity support) but this is probably a very distinct issue.
this would help you here maybe as you could at least see which user on which host created this cache entry.
yes, this could have helped a bit, especially since no Build Scan seems to be linked (usually originating build scans are referenced in the details panel with a link
(View origin Build Scan)
) but not for this specific entry. Although, since these are ephemeral CI runners, hostname & username will probably be re-used from a pool, but that would still be better than nothing.
The next to last idea I have is looking at log files of whatever you use as remote build cache. Maybe you can see in some access log or similar from which IP address that cache entry was stored and that way find out something.
I'm asking our infrastructure team for logs.
remote build-cache is and should always be read-only for each and everyone, except for CI.
I probably said it too quickly, but this is actually what we set up by default,
isPush
is is guarded by a property provider that only CI uses, but technically, anyone can "fake" being CI through env variables etc. I'm currently asking my colleagues for logs and if they use this "bypass" to push build cache (or if their AI agent did that on their behalf) but I don't think this has been ever bypassed on purpose. I somewhat remember a form on the admin site to set up authentication, but this was a while ago, way before the entire re-design. I'll try to see with the team managing our Develocity instance if this is something they can do, but given the Build Cache instance is used by many different teams across the world, setting an authentication wont be as simple as said 😅
v
Typical usage is (no idea about build cache node but I'd guess it supports it too) world-readable but authenticated write, then only the CI jobs get the write-credentials. Everyone else can use the cache read-only unauthenticated.
Although, since these are ephemeral CI runners, hostname & username will probably be re-used from a pool, but that would still be better than nothing.
It would at least have told you that it was coming from CI, or that it was coming from a colleague and from which.
🤞 1
s
And this exact scenario is what was brought up in this comment github.com/gradle/gradle/pull/28139#…
v
Yep, a comment after the PR was merged by an employee. Maybe it went unnoticed. Or maybe a user complaining in a separate issue has more weight. 🤷‍♂️
s
github.com/gradle/gradle/issues/38764 Lets see. Thanks again for all these info 🙏 I'm still waiting for our network logs and maybe we'll find out what pushed these empty entries.
👌 1