bazel coverage fails for all Java compiles with --experimental_worker_multiplex_sandboxing
Description of the bug:
With --collect_code_coverage and --experimental_worker_multiplex_sandboxing enabled together, every Java compile fails:
ERROR: BUILD.bazel:14:13: Building liblib.jar (2 source files) failed: (Exit 1):
java failed: error executing Javac command (from target //:lib) ...
java.io.FileNotFoundException:
<output_base>/bazel-workers/Javac-multiplex-worker-1-workdir/_main/
bazel-out/darwin_arm64-fastbuild/bin/liblib-paths-for-coverage.txt (No such file or directory)Both flags are required, and the toolchain must set javac_supports_worker_multiplex_sandboxing = True. rules_java's stock toolchains leave it at False, which makes --experimental_worker_multiplex_sandboxing a no-op for Javac, so the bug is invisible with a default setup.
--collect_code_coverage |
--experimental_worker_multiplex_sandboxing |
result |
|---|---|---|
| yes | yes | FileNotFoundException |
| yes | no | ok |
| no | yes | ok |
| no | no | ok |
Cause. <target>-paths-for-coverage.txt is a declared input of the Javac action and is staged correctly, into the per-request sandbox directory:
.../Javac-multiplex-worker-1-workdir/_main/__sandbox/1/_main/bazel-out/.../liblib-paths-for-coverage.txt ← staged here
.../Javac-multiplex-worker-1-workdir/_main/bazel-out/.../liblib-paths-for-coverage.txt ← opened hereA sandboxed multiplex worker's process working directory is the multiplexer's, while each request's inputs live under the __sandbox/<id>/ subdir named by WorkRequest.sandbox_dir. JavaBuilder resolves execroot-relative paths against that per-request dir (JavaLibraryBuildRequest.asPath), but the --post_processor jacoco <file> argument never went through it — JacocoInstrumentationProcessor.java does:
jar.addEntry(coverageInformation, Path.of(coverageInformation));Path.of on a relative path resolves against the process working directory, so the lookup lands one level of __sandbox/<id>/ too high. It only surfaces with both flags because the argument only exists under --collect_code_coverage, and the working directory only diverges from the request dir under multiplex sandboxing.
Why it matters. Path mapping skips persistent-worker actions unless the worker is sandboxed, so a repository that wants Java actions to participate in --experimental_output_paths=strip is pushed toward --experimental_worker_multiplex_sandboxing (as suggested in #22658). Doing that makes bazel coverage unusable. The only workarounds are giving up multiplex workers for Javac (--strategy=Javac=sandboxed) or leaving Java out of path mapping.
This is not a path-mapping bug on its own — it reproduces with --experimental_output_paths=off, at the unstripped path. With --experimental_output_paths=strip the same failure is reported at the stripped path, which makes it easy to misattribute.
Which category does this issue belong to?
Java Rules, Local Execution
What's the simplest, easiest way to reproduce this bug? Please provide a minimal example if possible.
bazel clean
bazel build --collect_code_coverage --experimental_worker_multiplex_sandboxing //:libbazel clean matters: neither flag is part of the action key, so a cached result from a previous invocation masks the failure.
BUILD.bazel:
load("@rules_java//toolchains:default_java_toolchain.bzl", "default_java_toolchain")
load("@rules_java//java:defs.bzl", "java_library")
# The only non-default ingredient.
default_java_toolchain(
name = "java_toolchain_sandboxed_mux",
javac_supports_worker_multiplex_sandboxing = True,
source_version = "17",
target_version = "17",
)
java_library(
name = "lib",
srcs = ["src/main/java/com/example/A.java"],
).bazelrc:
build --extra_toolchains=//:java_toolchain_sandboxed_mux_definition
build --java_language_version=17
build --java_runtime_version=remotejdk_17with MODULE.bazel depending on rules_java 9.3.0 and any trivial A.java.
Which operating system are you running Bazel on?
macOS 26.6.2, arm64
What is the output of bazel info release?
release 9.2.0
I verified the failure directly on 9.2.0 and 8.6.0. It is also reported on 8.7.0 and 8.8.0, which I did not run myself.
If this is a regression, please try to identify the Bazel commit where the bug was introduced with bazelisk --bisect.
Not a regression as far as I can tell — it appears to have been broken since sandboxed multiplex worker support was added for JavaBuilder. I did not bisect.
Have you found anything relevant by searching the web?
No existing issue found. #22658 is the discussion that recommends enabling javac_supports_worker_multiplex_sandboxing, which is what puts a repository into the broken configuration.
Any other information, logs, or outputs that you want to share?
Fix proposed in #31151: resolve the coverage-paths file against the request's working directory, like every other path in JavaLibraryBuildRequest. The jar entry name stays execroot-relative — JacocoCoverageRunner matches it by its -paths-for-coverage.txt suffix only, so the rewritten name that path mapping produces is harmless.
Source: bazelbuild/bazel