Windows: package DLL dependencies silently dropped ("no outside dependencies allowed") when venv is reached through an NTFS junction
Bug Report
Bug Description
When the project venv is reached through an NTFS junction (or any symlink) and Nuitka is invoked through the junction path, all DLL dependencies of packages that do not allow "outside dependencies" (e.g. PyQt6, and partly pywin32) are silently dropped, while Nuitka still reports Successfully created. The resulting binary (onefile and standalone) is missing Qt6Core.dll/Qt6Gui.dll/Qt6Widgets.dll and all Qt plugins, and crashes at startup:
Traceback (most recent call last):
...
ImportError: DLL load failed while importing QtWidgetsRoot cause: _detectBinaryDLLs (depends-exe layer) returns canonicalized dependency paths (junction/symlink resolved, drive letter lowercased, e.g. d:\projects\...), while _reduceToPythonPath compares them against inside_paths taken from raw, unresolved sys.path elements (junction form, e.g. C:\Users\FengYu\projects\...) using isFilenameBelowPath. The literal prefix comparison never matches, so every such dependency is removed with no outside dependencies allowed for 'PyQt6'.
Whether a given build survives depends on accidental string-prefix coincidences between the canonicalized paths and unresolved sys.path entries (e.g. when some sys.path entry happens to be a real-form prefix of the canonical tree), which makes the failure look flaky.
Proof of the mechanism: injecting a single canonical-form entry into sys.path (PYTHONPATH=<real site-packages>) flips a broken build into a fully healthy one; removing it breaks it again. --disable-dll-dependency-cache does not help — live scans canonicalize identically.
Reproduced with Nuitka 4.2.1 and current develop (4.3rc2), in both --mode=onefile and --mode=standalone.
️ Environment
1. Nuitka Version, Python Version, OS, and Platform
Stable:
4.2.1
Update status: up to date with stable release '4.2.1' (cached, 8 hours old).
Commercial: None
Python: 3.14.7 (main, Sep 1 2026, 14:17:30) [MSC v.1944 64 bit (AMD64)]
Flavor: Unknown
GIL: yes
Executable: ~\projects\Modifier-for-CaoCaoZhuan\.venv\Scripts\python.exe
OS: Windows
Arch: x86_64
WindowsRelease: 11develop:
4.3rc2
Update status: newer than stable release '4.2.1' (cached, 8 hours old).
Commercial: None
Python: 3.14.7 (main, Sep 1 2026, 14:17:30) [MSC v.1944 64 bit (AMD64)]
Flavor: Unknown
GIL: yes
OS: Windows
Arch: x86_64
WindowsRelease: 11(The --version run without --zig ends in the usual "cannot locate suitable C compiler" probe; compilation itself uses --zig with Zig 0.17.0.)
2. How Nuitka and Python were Installed
- Python: uv-managed CPython 3.14.7 (python-build-standalone), venv created with
uv venv --python 3.14inside the junctioned project tree - Nuitka 4.2.1: pip/PyPI into that venv; develop:
pip install https://github.com/Nuitka/Nuitka/archive/develop.zip - C backend:
--zig(Zig 0.17.0)
3. Relevant PyPI Packages and Versions
Nuitka 4.2.1 uv
PyQt6 6.11.0 uv
PyQt6-Qt6 6.11.2 uv
PyQt6_sip 13.12.0 uv
psutil 7.2.2 uv
pywin32 312 uv️ To Reproduce
Layout (the important part — C:\Users\FengYu\projects is an NTFS junction pointing to D:\projects):
C:\Users\FengYu\projects -> junction -> D:\projectsA PyQt6 program plus a venv containing PyQt6 live inside D:\projects\Modifier-for-CaoCaoZhuan, reachable as C:\Users\FengYu\projects\Modifier-for-CaoCaoZhuan through the junction.
1. "Hello World" Test
Basic compilation succeeds; the program also runs fine under standard Python regardless of path form (Qt OK).
2. Short, Self-Contained, Correct, Eligible (SSCCE) Example
# nuitka-project: --mode=onefile
# nuitka-project: --enable-plugin=pyqt6
import sys
from PyQt6.QtWidgets import QApplication, QLabel
app = QApplication(sys.argv)
label = QLabel("junction repro")
label.show()
print("Qt OK")The deterministic reproduction below uses a small real project with this same code as main.py (plus import mywindow), venv inside the junctioned tree. Note that minimal reproductions in fresh venvs can accidentally stay healthy — see "Additional Context" — the failure hinges on the junctioned venv layout shown here.
3. Nuitka Command Line Options
Broken — invoked through the junction (cwd in junction form; identical result from bash and PowerShell, in onefile and standalone, and with --disable-dll-dependency-cache):
Set-Location C:\Users\FengYu\projects\Modifier-for-CaoCaoZhuan
.\.venv\Scripts\python.exe -m nuitka --mode=onefile --enable-plugin=pyqt6 main.py
Nuitka-Onefile: Onefile payload compression ratio (26.55%) size 37401392 to 9929296.
# onefile crashes with ImportError; standalone dist contains zero qt6*.dllWorking — byte-identical command except cwd in canonical form:
Set-Location D:\projects\Modifier-for-CaoCaoZhuan
.\.venv\Scripts\python.exe -m nuitka --mode=onefile --enable-plugin=pyqt6 main.py
Nuitka-Onefile: Onefile payload compression ratio (28.45%) size 65421720 to 18610936.
# worksRescued — junction cwd again, but one canonical entry added to sys.path:
$env:PYTHONPATH = "D:\projects\Modifier-for-CaoCaoZhuan\.venv\Lib\site-packages"
.\.venv\Scripts\python.exe -m nuitka --mode=onefile --enable-plugin=pyqt6 main.py
# healthy again; instrumented _reduceToPythonPath shows removed == []Expected Behavior
DLL inclusion must not depend on whether sys.path reaches the venv through a junction. The "inside" test should compare equally-normalized forms: resolve the getPythonUnpackedSearchPath() entries the same way the dependency scanner resolves DLL paths (or compare real paths on both sides), so package DLLs that really live inside the venv are kept.
Actual Behavior & Output
Instrumented _detectBinaryDLLs / _reduceToPythonPath (Nuitka 4.2.1, junction cwd, --disable-dll-dependency-cache, i.e. live scan):
BINARY 'D:\\projects\\Modifier-for-CaoCaoZhuan\\.venv\\Lib\\site-packages\\PyQt6\\Qt6\\plugins\\imageformats\\qgif.dll'
DEPS ['d:\\projects\\modifier-for-caocaozhuan\\.venv\\lib\\site-packages\\pyqt6\\qt6\\bin\\qt6gui.dll',
'd:\\projects\\modifier-for-caocaozhuan\\.venv\\lib\\site-packages\\pyqt6\\qt6\\bin\\qt6core.dll',
'd:\\projects\\modifier-for-caocaozhuan\\.venv\\lib\\site-packages\\pyqt6\\qt6\\bin\\msvcp140.dll', ...]
use_cache=False
REDUCE inside=['C:\\Users\\FengYu\\projects\\Modifier-for-CaoCaoZhuan\\.venv\\Lib\\site-packages',
'C:\\Users\\FengYu\\projects\\...\\site-packages\\win32', ...] # unresolved junction form
kept=['c:\\\\users\\fengyu\\appdata\\roaming\\\\uv\\python\\cpython-3.14.7-windows-x86_64-none\\vcruntime140.dll', ...] # only MSVC redist rule survives
removed=['d:\\projects\\...\\qt6gui.dll', 'd:\\projects\\...\\qt6core.dll', ...]report.xml:
<excluded_dll name="d:\projects\modifier-for-caocaozhuan\.venv\Lib\site-packages\PyQt6\Qt6\bin\Qt6Core.dll" used_by="PyQt6\QtCore.pyd" reason="no outside dependencies allowed for 'PyQt6.QtCore'" />
<excluded_dll name="d:\projects\modifier-for-caocaozhuan\.venv\Lib\site-packages\pywin32_system32\pywintypes314.dll" used_by="pythoncom314.dll" reason="no outside dependencies allowed for 'win32api'" />Broken standalone main.dist contains zero qt6*.dll (31 files total, Qt6Core.dll/Qt6Gui.dll/Qt6Widgets.dll and all plugins missing).
↩️ Regression (if applicable)
Last known good/unknown: the comparison itself predates this report; the failure manifests only when a junction/symlink is involved and the dependency paths handed to _reduceToPythonPath are in canonical form. No bisect performed yet.
Additional Context (Optional)
- Code path:
freezer/Standalone.py_detectUsedDLLs→_reduceToPythonPath→isFilenameBelowPath(utils/FileOperations.py);inside_paths = getPythonUnpackedSearchPath()returns rawsys.pathelements (importing/Importing.py). --disable-dll-dependency-cache/--force-dll-dependency-cache-updatedo not change the outcome; live depends-exe scans canonicalize identically.- The same mechanism silently drops
pywintypes314.dllfor pywin32 (reasonno outside dependencies allowed for 'win32api') in affected layouts. - Workaround: build from the canonical (real) path, or ensure one canonical-form entry in
sys.path(e.g. viaPYTHONPATH).
Source: Nuitka/Nuitka