[9.3.0] Fix hang on include() cycles in MODULE.bazel (https://github.com/bazelbuild/bazel/pull/30737) - #30804
Merged
iancha1992 merged 1 commit intoAug 21, 2026
Conversation
### Description `ModuleFileFunction#advanceHorizon` compiles the module files named by the current horizon of `include()` statements and returns their own `include()` statements as the next horizon, without ever checking whether a file has already been compiled. A cycle among included files thus keeps the horizon non-empty forever, so that `ModuleFileFunction` spins in an endless loop, re-reading and re-compiling the same files until the build is interrupted. For the same reason, a "diamond" include structure results in a number of compilations that is exponential in the include depth. This PR skips include labels that have already been compiled (or that appear more than once in the same horizon) when advancing the horizon, which makes the loop terminate, and reports actual cycles as an error when the module file is executed, where the chain of `include()` calls is readily available: ``` ERROR: .../MODULE.bazel:1:8: include() cycle detected: //java:java.MODULE.bazel -> //python:python.MODULE.bazel -> //java:java.MODULE.bazel ``` Note that skipping already-compiled files does not change evaluation semantics: `include()` behaves as if the included file were textually inserted at the location of the call, and a file reachable via multiple `include()` paths is still executed once per path. Only the compilation of the file is shared. ### Motivation A single typo in a `MODULE.bazel` file (e.g. a `foo.MODULE.bazel` that includes itself) currently makes Bazel spin at 100% CPU with no output and no way to make progress. `include()` is only allowed in the root module and in modules with a non-registry override, so this is reachable from user-authored files. ### Build API Changes No ### Checklist - [x] I have added tests for the new use cases (if any). - [ ] I have updated the documentation (if applicable). ### Release Notes RELNOTES: Cycles between `MODULE.bazel` files connected via `include()` are now reported as an error instead of hanging the build. Closes bazelbuild#30737. PiperOrigin-RevId: 967604241 Change-Id: I8c0d8d082338e41c6b83712d452c82ad6da8517a
Wyverald
approved these changes
Aug 20, 2026
github-merge-queue
Bot
removed this pull request from the merge queue due to no response for status checks
Aug 20, 2026
Merged
via the queue into
bazelbuild:release-9.3.0
with commit Aug 21, 2026
9067c32
42 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
ModuleFileFunction#advanceHorizoncompiles the module files named by the current horizon ofinclude()statements and returns their owninclude()statements as the next horizon, without ever checking whether a file has already been compiled. A cycle among included files thus keeps the horizon non-empty forever, so thatModuleFileFunctionspins in an endless loop, re-reading and re-compiling the same files until the build is interrupted. For the same reason, a "diamond" include structure results in a number of compilations that is exponential in the include depth.This PR skips include labels that have already been compiled (or that appear more than once in the same horizon) when advancing the horizon, which makes the loop terminate, and reports actual cycles as an error when the module file is executed, where the chain of
include()calls is readily available:Note that skipping already-compiled files does not change evaluation semantics:
include()behaves as if the included file were textually inserted at the location of the call, and a file reachable via multipleinclude()paths is still executed once per path. Only the compilation of the file is shared.Motivation
A single typo in a
MODULE.bazelfile (e.g. afoo.MODULE.bazelthat includes itself) currently makes Bazel spin at 100% CPU with no output and no way to make progress.include()is only allowed in the root module and in modules with a non-registry override, so this is reachable from user-authored files.Build API Changes
No
Checklist
Release Notes
RELNOTES: Cycles between
MODULE.bazelfiles connected viainclude()are now reported as an error instead of hanging the build.Closes #30737.
PiperOrigin-RevId: 967604241
Change-Id: I8c0d8d082338e41c6b83712d452c82ad6da8517a
Commit ce039a7