#1905·YOLOX

`os.path.join` drive-letter heuristic silently breaks `_do_python_eval`'s annotation path on Windows

Author: HikeAndMapCreated Aug 29, 2026Updated Aug 29, 2026

VOCDetection._do_python_eval in yolox/data/datasets/voc.py builds a per-image annotation path template like this:

python
annopath = os.path.join(rootpath, "Annotations", "{:s}.xml")

On Windows, this silently discards rootpath and "Annotations", leaving annopath as just "{:s}.xml".

Root cause

Windows' os.path.join (ntpath.join) treats any path component whose second character is a colon as a drive-letter path (e.g. C:), and resets the join to start fresh from that component, discarding everything joined before it. The literal template string "{:s}.xml" has a colon at index 1 purely by coincidence of Python's format-spec syntax - ntpath can't tell that apart from an actual drive reference, so it collapses the whole path.

Confirmed directly:

python
>>> import os
>>> os.path.join(r"C:\a\b", "Annotations", "{:s}.xml")
'{:s}.xml'

This later crashes in voc_eval.py's parse_rec(annopath.format(imagename)) with FileNotFoundError, since the resulting path is just a bare filename with no directory. It only surfaces once evaluation actually reaches the per-class VOC mAP calculation (training and the results-file-writing step both succeed first), which may be why it's gone unnoticed - it only shows up on Windows, and only past that point.

Environment

  • OS: Windows 11
  • YOLOX: fresh clone of main, installed via pip install -e .
  • Triggered during tools/train.py's periodic evaluation (eval_interval), specifically inside _do_python_eval -> voc_eval -> parse_rec

Steps to reproduce

  1. On Windows, train on any VOC-format dataset far enough to trigger an evaluation pass.
  2. Traceback:
File ".../yolox/evaluators/voc_eval.py", line 92, in voc_eval
    recs[imagename] = parse_rec(annopath.format(imagename))
File ".../xml/etree/ElementTree.py", line 558, in parse
    source = open(source, "rb")
FileNotFoundError: [Errno 2] No such file or directory: '<imagename>.xml'

Suggested fix

Don't pass the format-string literal into os.path.join at all - build the directory first, then append the template via plain string concatenation:

python
annopath = os.path.join(rootpath, "Annotations") + os.sep + "{:s}.xml"

Verified this resolves the crash and parse_rec opens the correct file (tested against a real annotation file, with the resulting path formatting and parsing correctly).

Source: Megvii-BaseDetection/YOLOX