6.6.4 Locating Build-Service Failures
A message such as “the remote build failed” names a journey, not a failing component. The same visible symptom can begin in the source tree, Bazel's analysis, credentials, cache storage, scheduling, execution, result delivery, or the system that consumes the result. Reliable triage finds the earliest transition whose observed outcome contradicts its contract, then asks for the cheapest evidence that could prove that attribution wrong.
Follow transitions, not error words
Start with the correlated lifecycle from 6.6.1 Tracing a Build Across Services. Mark each transition as confirmed, contradicted, or unknown:
source/configuration
-> Bazel loading and analysis
-> remote authentication and capability negotiation
-> action-cache lookup and CAS input availability
-> scheduler admission and placement
-> worker input fetch and action execution
-> output upload and result publication
-> Bazel materialization and result interpretation
-> BEP/BES delivery
-> CI or dashboard consumption
This is not one mandatory pipeline. A local execution path can use a remote cache without a scheduler or remote worker. A successful action can finish even when BES delivery or a downstream dashboard fails. The Build Event Protocol (BEP) and Build Event Service (BES) are also separate: Bazel produces structured events, while BES transports those events to another service.1
The portable Remote Execution API defines capabilities, CAS and Action Cache
operations, Execute and streamed Operation messages, and the ActionResult
that refers to output blobs. It does not define a backend's admission queue,
placement algorithm, lease or attempt identity, worker identity, or worker
telemetry.2 Treat those scheduler and worker joins as contracts of the
implementation you operate, not as fields every REAPI service must expose.
Three lanes show a remote execution path, a local execution path that may still use a remote cache, and the result-delivery path from Bazel through local BEP, BES, and a consumer. Scheduler and worker evidence is marked as backend-specific. A status key says to continue after confirmed handoffs, investigate contradicted handoffs, and preserve unknown gaps without guessing.
The diagnostic rule is simple: do not jump past a transition you have not established. If Bazel never produced an action, executor telemetry cannot explain the failure. If a worker completed and published a result but the dashboard lacks it, “remote execution failed” is too broad. If a cache hit returns the wrong bytes, the symptom appears at the consumer, but the unsafe state may have been published by an earlier producer.
Build a layer hypothesis
Use one row per candidate boundary. Record the identity you followed, what evidence supports the boundary, what remains missing, and one next check that could falsify your current conclusion.
| Earliest suspect boundary | Evidence that supports it | First falsifying check |
|---|---|---|
| Source, loading, analysis, or configuration | No matching action identity exists; Bazel reports a source, loading, analysis, or configuration failure | Confirm whether Bazel created a matching action in BEP or execution evidence |
| Authentication or endpoint | The client attempted the configured service, but the request was rejected before cache or execution work was accepted | Check server-side request identity and authorization decision for the same invocation |
| AC/CAS | An action lookup, input upload, or referenced blob cannot be completed | Join the action or blob digest to backend lookup/upload evidence; try an isolated cache-bypass comparison |
| Scheduler | The action was accepted but no compatible placement was made | In backend-specific queue/admission records, check whether any worker received the same action or operation |
| Executor | A worker received the action, then input fetch, sandbox setup, tool execution, or output upload failed | Inspect backend-specific attempt and worker evidence and verify whether another attempt was placed |
| Network path | One side records a request or transfer while the other side has no matching observation, or transport terminates between two healthy endpoints | Correlate both endpoints and an intermediary observation; do not infer network failure from a client timeout alone |
| Test execution or Bazel test interpretation | A test action and its per-attempt TestResult exist, but the action failed or Bazel aggregated/interpreted the recorded outcome unexpectedly | Join the attempt, shard, and run to its test action and outputs; distinguish an executed test failure from client-side interpretation |
| BEP/BES | Bazel's action/build outcome exists, but announced events were not delivered completely | Compare a local BEP capture with BES acknowledgements and the consumer's received-event set |
| Result consumer | Complete events or artifacts reached the consumer boundary, but CI, a dashboard, or release logic interpreted or stored them incorrectly | Replay the same retained payload into the consumer or inspect its transformation and persistence record |
The table routes an investigation; it does not assign root cause automatically. For example, a scheduler queue can grow because executors are unhealthy, because no platform-compatible worker exists, or because one client submitted excessive work. A production incident in which one build submitted a very large set of long-running compile actions illustrates this distinction: the queue was the visible bottleneck, while the triggering workload originated at the client.3
Preserve uncertainty explicitly
A gap in telemetry is not evidence that the unobserved layer failed. Write the narrowest defensible result:
The action was accepted by the remote service, but no scheduler-admission or worker-receipt evidence was retained. The failure is bounded to that missing segment; it is not yet attributable to either scheduler or executor.
This wording is operationally useful. It tells the incident owner what is known, prevents an unsupported hand-off, and identifies the next instrumentation or query. It also avoids timestamp-only joins. Two records occurring near each other are weaker evidence than records joined by invocation, action, operation, result, or blob identity.
BEP completeness needs the same care. Its event graph announces child events, but a Bazel crash or failed transport can leave announced events unposted.4 Therefore a missing event can mean “the work did not happen,” “Bazel did not finish producing evidence,” or “delivery was incomplete.” Compare local BEP, BES acknowledgement, and consumer receipt before choosing among those layers.
Choose a discriminating check
A useful next check separates at least two plausible layers. “Retry the build” usually does not: it changes timing, placement, local state, and perhaps cache state at once. Prefer a bounded comparison that changes one boundary or inspects both sides of it.
- If local execution with the same declared action succeeds while remote-only execution fails before tool launch, inspect remote compatibility, input transfer, and worker setup—not the source-level test assertion.
- If a fresh client reads a remote result but cannot materialize a referenced blob, inspect the ActionResult-to-CAS relationship rather than scheduler latency.
- If a local BEP file contains the terminal result and the BES consumer does not, execution is no longer the leading hypothesis. Compare publication, acknowledgement, and consumer persistence.
- If cache access is suspected, first distinguish endpoint communication from action-identity mismatch. Bazel can report remote-cache read/write warnings for local execution, while execution-log comparison can expose different inputs, arguments, environment, or outputs across invocations.5,6
Do not repair yet. Clearing shared state, restarting a fleet, or enabling broad fallback may erase the producer, version, placement, or incomplete-transfer evidence needed to identify the boundary. First bound the failing segment and retain the first-failure record. 6.6.5 Classifying Failures Before Retrying then uses that location to decide whether the failure is deterministic, flaky, incompatible, transient, or corrupt—and whether retry is justified.
Classify: Bazel's local BEP file contains BuildFinished and the expected
test result. The BES-backed CI page remains “running,” and the remote execution
backend records successful action completion. Which layer should receive the
first investigation, and what would falsify that attribution?
Reveal
Start at BES delivery or the CI consumer, not at the executor. Compare the BES publication and acknowledgement record with the exact event set persisted by the consumer. Evidence that BES never received the terminal event moves the boundary toward client-to-BES transport; evidence that it acknowledged and stored the event moves it into consumer interpretation or persistence.
Once a build-cop or on-call rotation receives an incident, this bounded layer, the retained identities, and the first falsifying check form a much stronger handoff than an error-message category. H.7.8 Main Recovery develops the human ownership and coordination policy around that handoff.
Locate a build-service failure by walking the correlated lifecycle and finding the earliest transition that contradicts its contract. Attribute only as far as the evidence reaches, state missing trace segments as uncertainty, and choose a next check that distinguishes adjacent candidate layers. Preserve first-failure evidence before retry or repair; classification and recovery begin only after the failing boundary is bounded.
Check your understanding · 4 questions
1.Match each observation to the first boundary it supports investigating:
Drag each answer onto the matching prompt, or click an answer and then click a prompt
2.Classify each claim about local, cached, and remote execution paths:
Choose True or False for each sentence
3.Which statement correctly separates portable REAPI evidence from backend-specific evidence?
Select one answer
4.An action is accepted by the remote service, but the operation-to-attempt join was not retained. What is the defensible location result?
Select one answer
Footnotes
-
Build Event Protocol — BEP event production and the separate gRPC Build Event Service transport ↩
-
Remote APIs — protocol contracts for caching and remote execution — portable capabilities, CAS, Action Cache, Execute/Operation, and ActionResult contracts versus backend-defined scheduling and worker topology ↩
-
Post Mortems for 4 Years of Remote Execution - Ulf Adams, EngFlow Inc. — high-queue-age incident caused by a client workload that the service accepted normally ↩
-
Build Event Protocol — announced events can remain unposted after a Bazel crash or failed network transport ↩
-
Debugging Remote Cache Hits for Local Execution — cache endpoint warnings and separation of read and write investigation ↩
-
Debugging Remote Cache Hits for Remote Execution — execution-log comparison for action identity and cacheability diagnosis ↩