• BAZEL_BINDIR After Updating rules_ts to 3.5.2

    A puzzled developer at a laptop beside a large monitor displaying a missing BAZEL_BINDIR error

    Updating aspect_rules_ts to 3.5.2 broke the pipeline in one of the repositories I work on. The build also used aspect_rules_js and rules_docker. The error came from the JavaScript launcher:

    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 launcher needs BAZEL_BINDIR to find the root of Bazel’s output tree. Its suggestion to use . applies outside build actions. Here, the failure was in a build action, and setting the variable wherever I could did not help.

    The action environment

    Adding this line to .bazelrc got that build moving again:

    bash
    # .bazelrc
    build --incompatible_merge_fixed_and_default_shell_env

    The flag controls how Bazel combines two sources of environment variables for an action. According to the Bazel action API, when an action supplies both an env dictionary and use_default_shell_env = True:

    • With merging disabled, Bazel ignores the explicit env dictionary.
    • With merging enabled, Bazel merges that dictionary into the default action environment. Explicit values take precedence.

    The default environment includes platform-dependent variables and values supplied through --action_env. Enabling the flag does not make the action inherit every variable from an interactive shell.

    There is a concrete connection to this update. The 3.5.2 release notes include PR #620, which added use_default_shell_env = True to actions that already set BAZEL_BINDIR in env. With merging disabled, that combination drops the variable even though the rule supplies it.

    That explains the failure mode addressed by the flag. The original note did not record the Bazel version or a minimal reproducer, so it cannot establish the full configuration behind this particular failure. There is no need to assume that the update added a stricter launcher check.

    Bazel changed the flag’s default to true before the 7.0 release. If merging is already enabled, adding the same positive flag again changes nothing; a missing BAZEL_BINDIR then needs a different explanation.

    A separate rules_docker workaround

    Another problem involved platform selection for Docker image builds. The same configuration also used:

    bash
    # .bazelrc
    build --@io_bazel_rules_docker//transitions:enable=false

    The rules_docker README documents this switch for toolchain-resolution problems. It disables the rules’ platform transitions, which normally select toolchains for the container’s operating system and architecture. It does not repair the action environment or make host binaries suitable for a Linux container.

    Upstream now describes rules_docker as maintained on an as-needed basis and points to rules_oci as an alternative. Disabling the old transition leaves platform selection to the surrounding build configuration; the binaries placed in an image still have to match its target operating system and architecture.