ESP-IDF on Windows: Five Build Failures That Are Not Your Code
Non-ASCII paths, file(GLOB) not seeing new sources, the wrong Python, Git Bash vs PowerShell, and a build directory that cannot be moved. Each one wasted an afternoon; here is the two-line diagnosis for each.
None of the failures below have anything to do with the firmware. All of them produce error messages that look like they do. If idf.py build is failing and you have not changed anything that should break, run through this list first.
1. The project path contains non-ASCII characters
Symptom: the build fails somewhere in the CMake or Ninja stage with an error about a path, an encoding, or a file that “does not exist” although it plainly does.
Cause: the project lived under a directory with Chinese characters in its name (...\摄像头图传_通讯协议与源码\...). Parts of the ESP-IDF toolchain on Windows do not handle non-ASCII paths reliably.
Fix: keep the firmware project on a pure-ASCII path. Documentation, tools and backups can live anywhere; the directory that idf.py runs in cannot. On this project the firmware sits at E:\esp32-robot\xiaozhi-esp32\ and everything else, including folders with Chinese names, is beside it, not above it.
Spaces in the path are a milder version of the same problem and are also worth avoiding.
2. A new .cc file gives “undefined reference”
Symptom: you add motor_driver.cc to the board directory, the header is included fine, and the link step fails with undefined reference to MotorDriver::.... The file is right there.
Cause: the component’s CMakeLists.txt collects sources with file(GLOB ...). A glob is evaluated once, at configure time. An incremental idf.py build re-runs Ninja but does not re-run CMake configure, so the new file is not in the source list.
Fix:
idf.py reconfigure build
Diagnostic rule: new file + undefined reference = reconfigure first, read code second. This one bit me on three separate modules before it became reflex.
If you own the CMakeLists.txt, listing sources explicitly instead of globbing avoids the problem entirely, at the cost of one extra line per file.
3. Wrong Python version
Symptom: idf.py fails on start-up, or install.ps1 / export.ps1 complains about a missing or incompatible package.
Cause: the system had a newer Python (3.15) on PATH; ESP-IDF v5.5 wanted 3.12 and its virtual environment had been created with that.
Fix: let the ESP-IDF installer manage its own Python and always activate the environment through export.ps1 rather than relying on whatever python resolves to in a fresh shell. On this machine the working interpreter is %LOCALAPPDATA%\Programs\Python\Python312.
4. Running idf.py from Git Bash
Symptom:
MSys/Mingw is not supported
Cause: ESP-IDF’s Windows tooling detects an MSYS environment and refuses. Git Bash is MSYS.
Fix: use PowerShell (or the ESP-IDF PowerShell shortcut that the installer creates). Every flashing script and build command on this project is written for PowerShell for this reason.
5. Moving or copying the build/ directory
Symptom: after cloning the project to a new machine, or moving it to a different drive, the first build fails with errors referring to the old absolute path.
Cause: build/ is full of absolute paths from the machine and directory it was created on: CMake cache, Ninja files, compile commands.
Fix: delete build/ at the new location and do a full build. Do not try to be clever and copy it to save time; it costs more time than it saves. Add build/ to your backup exclusions.
The short version
| Symptom | First thing to try |
|---|---|
| Path/encoding errors | Move the project to an ASCII-only path |
| Undefined reference after adding a file | idf.py reconfigure build |
| Python package errors | Run export.ps1, check it is Python 3.12 |
MSys/Mingw is not supported |
Use PowerShell |
| Errors mentioning a path that no longer exists | Delete build/, full rebuild |
Each of these took an afternoon the first time and takes ten seconds now. Write them at the top of your project README; that is where I keep mine.