bazelbuild/bazel · error · ValueError
Duplicate output file: Both {} and {} map to {}
Error message
Duplicate output file: Both {} and {} map to {} What it means
Raised by create_embedded_tools.py while building the map of input files to output paths inside the embedded tools archive. Two distinct input files normalizing to the same output path would silently overwrite each other, so the script fails fast with this ValueError naming both colliding inputs and the shared output path.
Source
Thrown at src/create_embedded_tools.py:101
Raises:
ValueError: When two input files map to the same output file.
"""
with open(argsfile, 'r') as f:
input_files = sorted(set(x.strip() for x in f.readlines()))
result = {}
for input_file in input_files:
# If we have both a BUILD and a BUILD.tools file, take the latter only.
if (os.path.basename(input_file) == 'BUILD' and
input_file + '.tools' in input_files):
continue
# It's an error to have two files map to the same output file, because the
# result is hard to predict and can easily be wrong.
output_path = get_output_path(input_file)
if output_path in result:
raise ValueError(
'Duplicate output file: Both {} and {} map to {}'.format(
result[output_path], input_file, output_path))
result[output_path] = input_file
return result
def copy_jdk_into_archive(output_zip, archive_file, input_file):
"""Extract the JDK and adds it to the archive under jdk/*."""
def _replace_dirname(filename):
# Rename the first folder to 'jdk', because Bazel looks for a
# bundled JDK in the embedded tools using that folder name.
return 'jdk/' + '/'.join(filename.split('/')[1:])
# The JDK is special - it's extracted instead of copied.
if archive_file.endswith('.tar.gz'):
copy_tar_to_zip(output_zip, input_file, _replace_dirname)View on GitHub (pinned to e6e199d060)
Solutions
- Read the message: it names the two colliding inputs and the output path; remove or rename one of them.
- If the collision is a BUILD file, rename the intended override to BUILD.tools so the script takes it exclusively.
- Adjust get_output_path()/the input glob so each input maps to a unique output location.
- Audit recent directory moves for duplicate copies of the same tree.
Example fix
# before input_files = [..., "tools/foo/BUILD", "foo/BUILD"] # after # keep only one source for each output path; for overrides use: input_files = [..., "tools/foo/BUILD.tools"]
Defensive patterns
Strategy: validation
Validate before calling
outputs = {}
for f in input_files:
out = get_output_path(f)
assert out not in outputs, 'collision: %s vs %s -> %s' % (outputs.get(out), f, out)
outputs[out] = f Prevention
- Keep one canonical source tree per embedded output path.
- Use the BUILD.tools suffix convention for intentional BUILD overrides.
- Run the embedded-tools build immediately after reorganizing tool directories to catch collisions early.
When it happens
Trigger: Two entries in input_files whose get_output_path() results coincide — e.g. both 'third_party/foo/BUILD' and 'foo/BUILD' mapping to 'foo/BUILD', or the same file reachable under two different prefixes. The BUILD/BUILD.tools pair is the one sanctioned exception (BUILD.tools wins).
Common situations: Adding a new tools directory whose path prefix normalizes onto an existing embedded path; copy-pasting a glob that captures the same files twice via different roots; moving a directory without updating the other reference.
Related errors
- unknown archive type "%s"
- Proguard failed ({p.returncode})
- Invalid library proguard config parameters (these parameters
AI-assisted analysis of bazelbuild/bazel@e6e199d060 (2026-08-14).
Data as JSON: /api/errors/83df3c61c4a1b2a2.
Report an issue: GitHub.