One of repositories I work with uses Bazel as its build system. It leverages several Bazel rule packs as well. Such as: aspect_rules_ts, aspect_rules_js, @io_rules_docker and many others. Usually I keep all rules up to date and most updates are smooth. However, after updating rules_ts to version 3.5.2, I encountered the broken pipeline.
FATAL: aspect_rules_js[js_binary]: BAZEL_BINDIR must be set in environment to the makevar $(BINDIR) in js_binary build actions (which run in the execroot) so that build actions can change directories to always run out of the root of the Bazel output tree. See https://docs.bazel.build/versions/main/be/make-variables.html#predefined_variables. This is automatically set by 'js_run_binary' (https://github.com/aspect-build/rules_js/blob/main/docs/js_run_binary.md) which is the recommended rule to use for using a js_binary as the tool of a build action. If this is not a build action you can set the BAZEL_BINDIR to '.' instead to supress this error. For more context on this design decision, please read the aspect_rules_js README https://github.com/aspect-build/rules_js/tree/dbb5af0d2a9a2bb50e4cf4a96dbc582b27567155#running-nodejs-programs.
Pretty cryptic, huh?
The message explains that BAZEL_BINDIR needs to be defined so that build actions can reliably run from the root of the Bazel output tree.
It recommends setting BAZEL_BINDIR to . if it’s not a build action. Does it makes any sense after the update of typescript rules? Not really for me.
First, I tried following the advice from the error and setting BAZEL_BINDIR where possible, but the error persisted.
The solution was to add the following flag to .bazelrc:
# .bazelrc
build --incompatible_merge_fixed_and_default_shell_env
This flag instructs Bazel to merge the fixed shell environment with the default shell environment. In essence, it ensures that all necessary environment variables are correctly propagated during the build.
Another issue we faced was that our Docker image build was expecting linux binaries no matter the platform.
# .bazelrc
build --@io_bazel_rules_docker//transitions:enable=false
This flag disables a specific rule transition within the io_bazel_rules_docker rules. Normally, this transition would force the build to target Linux images.
Why These Flags Work
The BAZEL_BINDIR issue stems from how Bazel isolates build actions. When aspect_rules_js runs Node.js binaries, it needs to know where Bazel’s output directory is. The --incompatible_merge_fixed_and_default_shell_env flag ensures environment variables from your shell (including those set by Bazel itself) propagate correctly into build actions.
Without this flag, some environment variables get filtered out during the “sandboxing” of build actions. The rules_ts update likely added stricter checks that exposed this pre-existing configuration gap.
The Docker transitions issue is about cross-compilation. By default, rules_docker assumes you want Linux containers regardless of your host platform. On macOS or Windows, this triggers platform transitions that can conflict with other rules expecting native binaries. Disabling this transition tells Bazel: “I’ll handle platform targeting myself.”
Debugging Tips
When Bazel breaks after rule updates:
- Check the changelog — Aspect’s rules have detailed release notes
- Bisect versions — Go back one version at a time to find the breaking change
- Use
--subcommands— See exactly what Bazel is executing - Check GitHub issues — You’re rarely the first to hit these problems
Bazel’s error messages are verbose but often contain the exact flag or issue link you need.