#4033·Nuitka

Windows: package DLL dependencies silently dropped ("no outside dependencies allowed") when venv is reached through an NTFS junction

Author: abcfy2Created Sep 9, 2026Updated Sep 17, 2026
Labelsbugfactoryexcellent_report

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 QtWidgets

Root 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: 11

develop:

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.14 inside 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:\projects

A 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

python
# 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):

powershell
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*.dll

Working — byte-identical command except cwd in canonical form:

powershell
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.
# works

Rescued — junction cwd again, but one canonical entry added to sys.path:

powershell
$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:

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_reduceToPythonPathisFilenameBelowPath (utils/FileOperations.py); inside_paths = getPythonUnpackedSearchPath() returns raw sys.path elements (importing/Importing.py).
  • --disable-dll-dependency-cache / --force-dll-dependency-cache-update do not change the outcome; live depends-exe scans canonicalize identically.
  • The same mechanism silently drops pywintypes314.dll for pywin32 (reason no 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. via PYTHONPATH).