{"record":{"id":"83df3c61c4a1b2a2","repo":"bazelbuild/bazel","slug":"duplicate-output-file-both-and-map-to","errorCode":null,"errorMessage":"Duplicate output file: Both {} and {} map to {}","messagePattern":"Duplicate output file: Both (.+?) and (.+?) map to (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/create_embedded_tools.py","lineNumber":101,"sourceCode":"\n  Raises:\n    ValueError: When two input files map to the same output file.\n  \"\"\"\n  with open(argsfile, 'r') as f:\n    input_files = sorted(set(x.strip() for x in f.readlines()))\n\n    result = {}\n    for input_file in input_files:\n      # If we have both a BUILD and a BUILD.tools file, take the latter only.\n      if (os.path.basename(input_file) == 'BUILD' and\n          input_file + '.tools' in input_files):\n        continue\n\n      # It's an error to have two files map to the same output file, because the\n      # result is hard to predict and can easily be wrong.\n      output_path = get_output_path(input_file)\n      if output_path in result:\n        raise ValueError(\n            'Duplicate output file: Both {} and {} map to {}'.format(\n                result[output_path], input_file, output_path))\n      result[output_path] = input_file\n\n  return result\n\n\ndef copy_jdk_into_archive(output_zip, archive_file, input_file):\n  \"\"\"Extract the JDK and adds it to the archive under jdk/*.\"\"\"\n\n  def _replace_dirname(filename):\n    # Rename the first folder to 'jdk', because Bazel looks for a\n    # bundled JDK in the embedded tools using that folder name.\n    return 'jdk/' + '/'.join(filename.split('/')[1:])\n\n  # The JDK is special - it's extracted instead of copied.\n  if archive_file.endswith('.tar.gz'):\n    copy_tar_to_zip(output_zip, input_file, _replace_dirname)","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/bazelbuild/bazel/blob/e6e199d0601a244511b4cf18c8b2828aa73db1fd/src/create_embedded_tools.py#L83-L119","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"# before\ninput_files = [..., \"tools/foo/BUILD\", \"foo/BUILD\"]\n\n# after\n# keep only one source for each output path; for overrides use:\ninput_files = [..., \"tools/foo/BUILD.tools\"]","handlingStrategy":"validation","validationCode":"outputs = {}\nfor f in input_files:\n    out = get_output_path(f)\n    assert out not in outputs, 'collision: %s vs %s -> %s' % (outputs.get(out), f, out)\n    outputs[out] = f","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["build","packaging","embedded-tools","duplicate"],"backgroundTag":null,"analyzedSha":"e6e199d0601a244511b4cf18c8b2828aa73db1fd","analyzedAt":"2026-08-14T10:24:27.848Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}