/tmp/example-workspace/WORKSPACE.bazel:13:13
Explains Bazel's WORKSPACE chunking behavior where dependency resolution depends on load statement boundaries.
What this file does
Explains Bazel's WORKSPACE chunking behavior where dependency resolution depends on load statement boundaries.
When to use it
- Debugging wrong version of an external repo in a large WORKSPACE
- Understanding why first or last definition wins depending on load statements
- Avoiding misleading results from
bazel queryfor external repos
Assumes this stack
layout: post title: Bazel WORKSPACE chunking date: 2024-08-29 10:36 -0700 excerpt_separator: <!--more-->
I have been doing quite a lot of Bazel for $DAYJOB$; and it's definitely got it's fair share of warts.
I have my own misgivings of it's migration to bzlmod and it converging to a standard-issue dependency-management style tool.
We have yet to transition to MODULE.bazel and our codebase is quite large. As you'd expect, we hit quite a lot of diamond dependency issues & specifically with external repositories in our WORKSPACE file.
A surprising implementation detail I recently learned was how Bazel does dependency resolution for external repositories in WORKSPACE.
<!--more-->To demonstrate, let's see a quick example. I have two sample workspaces that export a single target //:version that will either have 1.0 or 2.0.
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive", "http_file")
http_archive(
name = "workspace",
url = "file:///tmp/example-workspace/workspace-1.0.tar.gz",
strip_prefix = "workspace-1.0",
)
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
http_archive(
name = "workspace",
url = "file:///tmp/example-workspace/workspace-2.0.tar.gz",
strip_prefix = "workspace-2.0",
)
What is the version of @workspace//:version you expect ? 🤔
$ bazel build @workspace//:version
INFO: Analyzed target @workspace//:version (1 packages loaded, 1 target configured).
INFO: Found 1 target...
Target @workspace//:version up-to-date:
bazel-bin/external/workspace/VERSION
INFO: Elapsed time: 0.078s, Critical Path: 0.00s
INFO: 1 process: 1 internal.
INFO: Build completed successfully, 1 total action
$ cat bazel-bin/external/workspace/VERSION
1.0
Huh. 😳. Okay.
So in this example it looks like it's first version wins.
Let's try another slightly different example.
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive", "http_file")
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
http_archive(
name = "workspace",
url = "file:///tmp/example-workspace/workspace-1.0.tar.gz",
strip_prefix = "workspace-1.0",
)
http_archive(
name = "workspace",
url = "file:///tmp/example-workspace/workspace-2.0.tar.gz",
strip_prefix = "workspace-2.0",
)
What is the version of @workspace//:version you expect now ? 🤔
$ bazel build @workspace//:version
INFO: Analyzed target @workspace//:version (1 packages loaded, 2 targets configured).
INFO: Found 1 target...
Target @workspace//:version up-to-date:
bazel-bin/external/workspace/VERSION
INFO: Elapsed time: 0.145s, Critical Path: 0.02s
INFO: 2 processes: 1 internal, 1 linux-sandbox.
INFO: Build completed successfully, 2 total actions
$ cat bazel-bin/external/workspace/VERSION
2.0
😳 2.0
So now it looks like it's last one wins? 🤨
Turns out the version selection in Bazel is a little more complex.
🕵️ The WORKSPACE file into chunks separated by load statements. For a repo named X (@workspace// in our case), the last definition of X in the first chunk that contains a definition of X is the winner
If you ever face an issue of having the wrong version of an external repoitory, the thing to do is move it up load statements until you find the chunk that is defining it.
I found references to this on some GitHub issues & was helped out by @Wyverald on Slack but thought a clear concise example to demonstrate it was interesting.
If you want to play with the example above, I've uploaded it to bazel-workspace-chunking on GitHub.
❗ Don't trust bazel query //external:workspace --output build to see the version.
You might be tempted to run the above query but it gives incorrect results.
$ bazel build @workspace//:version
INFO: Analyzed target @workspace//:version (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
Target @workspace//:version up-to-date:
bazel-bin/external/workspace/VERSION
INFO: Elapsed time: 0.057s, Critical Path: 0.00s
INFO: 1 process: 1 internal.
INFO: Build completed successfully, 1 total action
$ cat bazel-bin/external/workspace/VERSION
1.0
$ bazel query //external:workspace --output build
# /tmp/example-workspace/WORKSPACE.bazel:13:13
http_archive(
name = "workspace",
url = "file:///tmp/example-workspace/workspace-2.0.tar.gz",
strip_prefix = "workspace-2.0",
)
# Rule workspace instantiated at (most recent call last):
# /tmp/example-workspace/WORKSPACE.bazel:13:13 in <toplevel>
# Rule http_archive defined at (most recent call last):
# /home/fmzakari/.cache/bazel/_bazel_fmzakari/c72d1df8de0701fb5f44d35dec4b70b5/external/bazel_tools/tools/build_defs/repo/http.bzl:372:31 in <toplevel>
Loading: 0 packages loaded
Even when the VERSION was still 1.0, the query simply gives back the last version cited in the WORKSPACE file.
What's inside
Two code examples, console output, and explanation of chunk-based resolution plus a query caveat
Change this for your project
- Replace
file:///tmp/example-workspace/workspace-1.0.tar.gzwith your own archive URLs - Replace
@workspace//:versionwith your actual target label
Where it goes
Keep it in your repository where the agent or team that needs it will read it.
Worth borrowing
- Use load statements to control which chunk defines a repo
- Verify version by building the target, not by querying
Related Documents
基于命题分块以增强RAG
Implements proposition chunking for RAG: decomposes documents into atomic facts, evaluates quality, and compares retrieval against standard chunking.
TileMap Chunk Manager
Divides a large tilemap into fixed-size chunks, loading and rendering only those visible to reduce memory and draw calls by over 99%.
🤖 n8n AI Agent Mastery Course 2025
Serves as the main README for an n8n course repository, listing 10+ AI agent workflow projects with links to YouTube tutorials and Docker setup instructions.
Message Chunking for MCP stdio Transport
Explains macOS 64KB pipe write limit and provides a chunking layer for MCP stdio transport that splits and reassembles large messages transparently.