5.6.4 Toolchain Resolution Debugging

recommended

A toolchain-resolution failure is an analysis problem with a finite set of inputs: a requested toolchain type, registered toolchain candidates, a target platform, and available execution platforms. --toolchain_resolution_debug exposes Bazel's decisions across those inputs. Use the trace to eliminate registration, constraint, and ordering hypotheses in a controlled sequence.

1.1.10 Common Error Messages gives the first-response version of this diagnosis. This article assumes the platform and resolution model from 4.6.3 Toolchain Resolution and concentrates on the investigation.

Scope the trace before reading it

Start with the exact command that fails. Preserve every configuration flag, especially --platforms, --extra_execution_platforms, --extra_toolchains, and named .bazelrc configs. Resolution is performed for a configured target, so changing those inputs while adding diagnostics can change the result you are trying to explain.

Use the toolchain type printed by the error as the first filter:

bazel build //app:release \
  --toolchain_resolution_debug='//toolchains:compiler_type'

The value is a regular expression, not a toolchain label argument. Bazel checks it against toolchain-type labels and specific target labels to decide which resolution events to print. Commas separate multiple regular expressions. .* enables every resolution trace, which is useful only after a narrow filter fails to capture the relevant invocation.1 Quote the value so that the shell does not interpret regex metacharacters.

This distinction matters when one toolchain type is resolved for many targets. Filtering on the type reveals all those resolutions. Filtering on //app:release focuses on resolutions associated with that target. A type label from an error is usually the safest starting point because it names the missing requirement directly.2

Read the trace in four layers

Read one resolution block from its outer context inward. Do not begin with the first line containing “rejected.”

  1. Consumer and requested type. Confirm that the block belongs to the failing configured target or its execution group, and that it resolves the expected toolchain_type. A rule with execution groups can perform separate resolutions and select different execution platforms for each group.2
  2. Target platform. Record the target-platform label in the block. A candidate's target_compatible_with constraints describe what it can produce for. They are matched against this platform.
  3. Execution-platform candidates. Record the platforms Bazel actually considers. A candidate's exec_compatible_with constraints describe where its tools can run. The target or rule's own exec_compatible_with may have removed execution platforms before toolchain candidates are compared.2
  4. Candidate outcome and final selection. For each registered toolchain() candidate, note whether it was skipped for target constraints, could not run on an execution platform, or was assigned. Then read the recap showing which execution platforms remain viable and which platform wins.3

The target and execution checks answer different questions. A Linux-hosted cross-compiler might run on a Linux executor (exec_compatible_with) while producing an Android ARM artifact (target_compatible_with). Describing both lists as constraints “for the target platform” hides the most common cross-compilation failure.

Constraint matching is conjunctive but not exact-set equality. Every value listed by the toolchain must be present on the corresponding platform, either explicitly or by a constraint setting's default. Extra values on the platform from settings the toolchain does not mention do not reject it.2

A diagnostic workflow that converges

Use the trace as an evidence table, then inspect definitions only where the table has a gap.

1. Prove that a candidate was registered

If the trace contains no candidate for the required type, stop comparing constraints. Verify the ruleset's documented registration path and the active dependency mode. Registration can come from register_toolchains() in the module graph or from --extra_toolchains. Merely defining a toolchain implementation and a generic toolchain() target does not register it.2

Check whether a root-module tag, extension repository, use_repo(), or ruleset option suppressed registration. Real rulesets deliberately support modes where repositories exist but automatic toolchain registration is disabled, producing the same “no matching toolchains” symptom.4 Also run with --announce_rc when local and CI results differ: an rc file may be adding or removing platform and registration flags.

2. Verify the platform inputs

Copy the target-platform label and execution-platform labels from the trace. Inspect those platform() targets and the effective command-line flags. --platforms selects the target platform. register_execution_platforms() and --extra_execution_platforms contribute execution-platform candidates, and Bazel also includes the host platform.2

Use bazel config only for configuration values it actually reports. It can confirm the effective --platforms option for a configuration hash, but it is not a registry browser and does not replace the resolution trace's list of execution platforms. 5.2.4 bazel config — Configuration Inspection shows how to obtain and inspect that hash.

3. Compare each constraint on the correct axis

For a candidate that appears but is skipped, expand its toolchain() declaration and make two comparisons:

DeclarationCompare withDiagnostic question
target_compatible_withtarget platformCan this toolchain produce for the requested target?
exec_compatible_witheach execution platformCan this toolchain's tools run on this executor?
target_settingscurrent build configurationIs this candidate enabled by the active settings?

Compare canonical constraint_value labels, including repository identity—not just similar-looking names such as two independently declared linux values. If a platform omits a setting, check whether that constraint_setting declares a default before calling the value missing.2

4. Explain a surprising winner with order

When several candidates match, the problem is no longer compatibility. Resolution uses ordered candidate and execution-platform lists. --extra_toolchains has higher priority than module registrations. Registration origin and declaration order then affect the candidate order. Execution-platform order decides among viable platforms according to the resolution rules.2,3

Do not “fix” this by making constraints falsely specific. First identify the two candidates that both match, then either make their intended domains genuinely disjoint or change registration priority deliberately. Re-run the same narrow trace and verify that the winner changed for the reason you intended.

5. Confirm the resolved dependency

After analysis succeeds, use configured-query evidence rather than assuming the trace implies the final graph:

bazel cquery 'deps(//app:release, 1)' --transitions=lite \
  --platforms=//platforms:android_arm64

The transition output marks toolchain dependency edges. Keep the same flags as the build. A cquery under a different configuration proves a different resolution. 5.2.2 bazel cquery — Configured Graph develops this configured-graph inspection workflow.2

Keep the diagnosis attached to one resolution

Large builds can resolve the same type repeatedly for different configured targets and execution groups. Save the failing label, configuration, requested type, target platform, considered execution platforms, candidate rejection reason, and registration source together. That small record prevents a rejection from one resolution block being used to explain another.

Bazel 7 made the trace substantially easier to follow by grouping output per algorithm invocation, indenting nested decisions, and avoiding some duplicate work in the log.3 Exact wording remains version-sensitive. Treat the structure and labels as evidence, but do not build automation that parses human-oriented debug text as a stable interface.

key takeaway

Filter --toolchain_resolution_debug with the toolchain type from the failure, then read one resolution block from consumer to target platform, execution platforms, candidates, and final selection. The regex can match toolchain types or target labels. .* is an escalation, not the default investigation.

If no candidate appears, repair registration. If a candidate is rejected, compare target_compatible_with to the target platform and exec_compatible_with to each execution platform. If several candidates survive, explain the winner from registration and platform order, then confirm the resolved edge with cquery under the same configuration.

Check your understanding · 4 questions

1.A compiler runs on Linux executors and produces Android ARM64 binaries. Which constraint assignment expresses those two roles?

Select one answer

2.A large build resolves one compiler type for many targets. Which debug filters are useful first steps?

Select all that apply

3.Match each trace symptom to the next diagnostic check:

Drag each answer onto the matching prompt, or click an answer and then click a prompt

Answers
No candidate appears for the requested type
A candidate fails its target constraints
A candidate cannot run on any considered executor
Several candidates remain compatible

4.After collecting a narrow resolution trace, decide whether each follow-up claim is correct.

Choose True or False for each sentence

bazel config can confirm the effective --platforms value for the relevant configuration.
bazel config replaces the trace as a complete list of registered execution platforms.
cquery --transitions=lite can confirm which configured dependency edge came from toolchain resolution.
A cquery run with different build flags confirms the resolution observed in the failing build.
0 of 4 answered

Footnotes

  1. Command-Line Reference — current --toolchain_resolution_debug syntax, target/type regex matching, comma-separated filters, and default behavior

  2. Toolchains — resolution inputs, constraint semantics, registrations, execution groups, debug filtering, and configured-query follow-up 1 2 3 4 5 6 7 8 9

  3. Improved --Toolchain_resolution_debug'ing - Malte Poll, Modus Create — trace structure since Bazel 7 and the relationship between candidate assignment and execution-platform selection 1 2 3

  4. rules_foreign_cc — foreign C/C++ build-system integration — current ruleset examples where explicit or disabled registration leads to no-matching-toolchains failures