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.

Find the first handoff whose evidence stops agreeing.
Builds can take different execution paths, but every path ends in result delivery.

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.

Triage question: Which adjacent pair has identities and observations that cannot both be true?
REMOTE EXECUTION PATH
Bazel
Did analysis create the action?
source and configuration
invocation + action digest
Portable REAPI
Was work accepted?
AC, CAS, Execute, Operation
instance + digest + operation
Backend-specific
Where did the attempt stop?
admission, placement, worker
operation → attempt → worker
Bazel result
Did bytes and status arrive?
outputs and test attempts
ActionResult + referenced blobs
LOCAL OR CACHE-ONLY BYPASS
Bazel
Did Bazel choose local execution?
remote cache remains optional
no scheduler required
Local action
Did the local action complete?
cache may read or write
local attempt evidence
Bazel result
Did Bazel interpret the outcome?
same delivery path follows
status + outputs + TestResult
RESULT DELIVERY BRANCH
Local BEP
Did Bazel produce the event?
announced event graph
invocation + event ID
BES transport
Was the event received?
delivery and acknowledgement
receipt ≠ complete stream
Consumer
Was the result interpreted correctly?
CI, dashboard, or release
event set + artifact availability
CONFIRMED
Continue to the next handoff.
CONTRADICTED
Investigate this boundary first.
UNKNOWN
Preserve the gap. Do not guess.
Stop at the earliest contradiction. The place where a symptom appears can be later than the handoff that first became inconsistent.

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 boundaryEvidence that supports itFirst falsifying check
Source, loading, analysis, or configurationNo matching action identity exists; Bazel reports a source, loading, analysis, or configuration failureConfirm whether Bazel created a matching action in BEP or execution evidence
Authentication or endpointThe client attempted the configured service, but the request was rejected before cache or execution work was acceptedCheck server-side request identity and authorization decision for the same invocation
AC/CASAn action lookup, input upload, or referenced blob cannot be completedJoin the action or blob digest to backend lookup/upload evidence; try an isolated cache-bypass comparison
SchedulerThe action was accepted but no compatible placement was madeIn backend-specific queue/admission records, check whether any worker received the same action or operation
ExecutorA worker received the action, then input fetch, sandbox setup, tool execution, or output upload failedInspect backend-specific attempt and worker evidence and verify whether another attempt was placed
Network pathOne side records a request or transfer while the other side has no matching observation, or transport terminates between two healthy endpointsCorrelate both endpoints and an intermediary observation; do not infer network failure from a client timeout alone
Test execution or Bazel test interpretationA test action and its per-attempt TestResult exist, but the action failed or Bazel aggregated/interpreted the recorded outcome unexpectedlyJoin the attempt, shard, and run to its test action and outputs; distinguish an executed test failure from client-side interpretation
BEP/BESBazel's action/build outcome exists, but announced events were not delivered completelyCompare a local BEP capture with BES acknowledgements and the consumer's received-event set
Result consumerComplete events or artifacts reached the consumer boundary, but CI, a dashboard, or release logic interpreted or stored them incorrectlyReplay 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.

think

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.

key takeaway

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

Answers
Loading fails before Bazel creates a matching action
A test action runs and its per-attempt TestResult records failure
An ActionResult arrives but one referenced output blob is unavailable
An accepted Operation has no recorded compatible placement

2.Classify each claim about local, cached, and remote execution paths:

Choose True or False for each sentence

A locally executed action can still read from or write to a remote cache.
Every remote-cache problem must pass through a scheduler and remote worker.
A remote Execute path can add backend-specific admission, placement, and worker boundaries.
A successful remote worker attempt proves that a downstream CI consumer received a complete result.

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

0 of 4 answered

Footnotes

  1. Build Event Protocol — BEP event production and the separate gRPC Build Event Service transport

  2. 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

  3. 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

  4. Build Event Protocol — announced events can remain unposted after a Bazel crash or failed network transport

  5. Debugging Remote Cache Hits for Local Execution — cache endpoint warnings and separation of read and write investigation

  6. Debugging Remote Cache Hits for Remote Execution — execution-log comparison for action identity and cacheability diagnosis