Corrupted Python installation

I relatively often experience that Python breaks when I use it in Grasshopper.
It’s extremely frustrating because I have to reinstall packages again. And then it may happen again next time I need to install a missing package.

packages are installed through the python component with #r: matplotlib for example
See this log


Info 12/15/2025 13:13:57 [RhinoCode] Installing "networkx"
Info 12/15/2025 13:13:57 [RhinoCode] Running process: C:\Users\timoharboe-zollner\.rhinocode\py39-rh8\python.exe -I -m pip --isolated --disable-pip-version-check install --target "C:\Users\timoharboe-zollner\.rhinocode\py39-rh8\site-envs\default-XtRtGpEC" --progress-bar off --upgrade --no-warn-script-location --retries 5 --timeout 15 "networkx"
Info 12/15/2025 13:14:03 [RhinoCode] Running process: C:\Users\timoharboe-zollner\.rhinocode\py39-rh8\python.exe -I -m pip --isolated --disable-pip-version-check list --path "C:\Users\timoharboe-zollner\.rhinocode\py39-rh8\site-envs\default-XtRtGpEC" --format freeze
Info 12/15/2025 13:14:04 [RhinoCode] Collecting networkx
  Using cached networkx-3.2.1-py3-none-any.whl.metadata (5.2 kB)
Using cached networkx-3.2.1-py3-none-any.whl (1.6 MB)
Installing collected packages: networkx
Successfully installed networkx-3.2.1

Info 12/15/2025 13:14:17 [RhinoCode] Installing "FPDF"
Info 12/15/2025 13:14:17 [RhinoCode] Running process: C:\Users\timoharboe-zollner\.rhinocode\py39-rh8\python.exe -I -m pip --isolated --disable-pip-version-check install --target "C:\Users\timoharboe-zollner\.rhinocode\py39-rh8\site-envs\default-XtRtGpEC" --progress-bar off --upgrade --no-warn-script-location --retries 5 --timeout 15 "FPDF"
Info 12/15/2025 13:14:18 [RhinoCode] Running process: C:\Users\timoharboe-zollner\.rhinocode\py39-rh8\python.exe -I -m pip --isolated --disable-pip-version-check list --path "C:\Users\timoharboe-zollner\.rhinocode\py39-rh8\site-envs\default-XtRtGpEC" --format freeze
Info 12/15/2025 13:14:18 [RhinoCode] Collecting FPDF
  Using cached fpdf-1.7.2-py2.py3-none-any.whl
Installing collected packages: FPDF
Successfully installed FPDF-1.7.2

Info 12/15/2025 13:14:27 [RhinoCode] Installing "matplotlib"
Info 12/15/2025 13:14:27 [RhinoCode] Running process: C:\Users\timoharboe-zollner\.rhinocode\py39-rh8\python.exe -I -m pip --isolated --disable-pip-version-check install --target "C:\Users\timoharboe-zollner\.rhinocode\py39-rh8\site-envs\default-XtRtGpEC" --progress-bar off --upgrade --no-warn-script-location --retries 5 --timeout 15 "matplotlib"
Error 12/15/2025 13:14:45 [RhinoCode] Process exited with code 2
Error 12/15/2025 13:14:45 [RhinoCode] pip install error | Collecting matplotlib
  Using cached matplotlib-3.9.4-cp39-cp39-win_amd64.whl.metadata (11 kB)
Collecting contourpy>=1.0.1 (from matplotlib)
  Using cached contourpy-1.3.0-cp39-cp39-win_amd64.whl.metadata (5.4 kB)
Collecting cycler>=0.10 (from matplotlib)
  Using cached cycler-0.12.1-py3-none-any.whl.metadata (3.8 kB)
Collecting fonttools>=4.22.0 (from matplotlib)
  Downloading fonttools-4.60.2-cp39-cp39-win_amd64.whl.metadata (115 kB)
Collecting kiwisolver>=1.3.1 (from matplotlib)
  Using cached kiwisolver-1.4.7-cp39-cp39-win_amd64.whl.metadata (6.4 kB)
Collecting numpy>=1.23 (from matplotlib)
  Using cached numpy-2.0.2-cp39-cp39-win_amd64.whl.metadata (59 kB)
Collecting packaging>=20.0 (from matplotlib)
  Using cached packaging-25.0-py3-none-any.whl.metadata (3.3 kB)
Collecting pillow>=8 (from matplotlib)
  Using cached pillow-11.3.0-cp39-cp39-win_amd64.whl.metadata (9.2 kB)
Collecting pyparsing>=2.3.1 (from matplotlib)
  Using cached pyparsing-3.2.5-py3-none-any.whl.metadata (5.0 kB)
Collecting python-dateutil>=2.7 (from matplotlib)
  Using cached python_dateutil-2.9.0.post0-py2.py3-none-any.whl.metadata (8.4 kB)
Collecting importlib-resources>=3.2.0 (from matplotlib)
  Using cached importlib_resources-6.5.2-py3-none-any.whl.metadata (3.9 kB)
Collecting zipp>=3.1.0 (from importlib-resources>=3.2.0->matplotlib)
  Using cached zipp-3.23.0-py3-none-any.whl.metadata (3.6 kB)
Collecting six>=1.5 (from python-dateutil>=2.7->matplotlib)
  Using cached six-1.17.0-py2.py3-none-any.whl.metadata (1.7 kB)
Using cached matplotlib-3.9.4-cp39-cp39-win_amd64.whl (7.8 MB)
Using cached contourpy-1.3.0-cp39-cp39-win_amd64.whl (211 kB)
Using cached cycler-0.12.1-py3-none-any.whl (8.3 kB)
Downloading fonttools-4.60.2-cp39-cp39-win_amd64.whl (1.5 MB)
Using cached importlib_resources-6.5.2-py3-none-any.whl (37 kB)
Using cached kiwisolver-1.4.7-cp39-cp39-win_amd64.whl (55 kB)
Using cached numpy-2.0.2-cp39-cp39-win_amd64.whl (15.9 MB)
Using cached packaging-25.0-py3-none-any.whl (66 kB)
Using cached pillow-11.3.0-cp39-cp39-win_amd64.whl (7.0 MB)
Using cached pyparsing-3.2.5-py3-none-any.whl (113 kB)
Using cached python_dateutil-2.9.0.post0-py2.py3-none-any.whl (229 kB)
Using cached six-1.17.0-py2.py3-none-any.whl (11 kB)
Using cached zipp-3.23.0-py3-none-any.whl (10 kB)
Installing collected packages: zipp, six, pyparsing, pillow, packaging, numpy, kiwisolver, fonttools, cycler, python-dateutil, importlib-resources, contourpy, matplotlib
Successfully installed contourpy-1.3.0 cycler-0.12.1 fonttools-4.60.2 importlib-resources-6.5.2 kiwisolver-1.4.7 matplotlib-3.9.4 numpy-2.0.2 packaging-25.0 pillow-11.3.0 pyparsing-3.2.5 python-dateutil-2.9.0.post0 six-1.17.0 zipp-3.23.0

ERROR: Exception:
Traceback (most recent call last):
  File "C:\Users\timoharboe-zollner\.rhinocode\py39-rh8\lib\site-packages\pip\_internal\cli\base_command.py", line 107, in _run_wrapper
    status = _inner_run()
  File "C:\Users\timoharboe-zollner\.rhinocode\py39-rh8\lib\site-packages\pip\_internal\cli\base_command.py", line 98, in _inner_run
    return self.run(options, args)
  File "C:\Users\timoharboe-zollner\.rhinocode\py39-rh8\lib\site-packages\pip\_internal\cli\req_command.py", line 85, in wrapper
    return func(self, options, args)
  File "C:\Users\timoharboe-zollner\.rhinocode\py39-rh8\lib\site-packages\pip\_internal\commands\install.py", line 520, in run
    self._handle_target_dir(
  File "C:\Users\timoharboe-zollner\.rhinocode\py39-rh8\lib\site-packages\pip\_internal\commands\install.py", line 575, in _handle_target_dir
    shutil.rmtree(target_item_dir)
  File "C:\Users\timoharboe-zollner\.rhinocode\py39-rh8\lib\shutil.py", line 749, in rmtree
    return _rmtree_unsafe(path, onerror)
  File "C:\Users\timoharboe-zollner\.rhinocode\py39-rh8\lib\shutil.py", line 622, in _rmtree_unsafe
    _rmtree_unsafe(fullname, onerror)
  File "C:\Users\timoharboe-zollner\.rhinocode\py39-rh8\lib\shutil.py", line 627, in _rmtree_unsafe
    onerror(os.unlink, fullname, sys.exc_info())
  File "C:\Users\timoharboe-zollner\.rhinocode\py39-rh8\lib\shutil.py", line 625, in _rmtree_unsafe
    os.unlink(fullname)
PermissionError: [WinError 5] Access is denied: 'C:\\Users\\timoharboe-zollner\\.rhinocode\\py39-rh8\\site-envs\\default-XtRtGpEC\\numpy\\linalg\\_umath_linalg.cp39-win_amd64.pyd'


Info 12/15/2025 13:27:48 [RhinoCode] Stubs already exist for System.ValueTuple, Version=8.0.0.0

It sounds more like a OS specific issue. Could it be that you are running Rhino as a machine installation or with a different user and don’t get access while doing so? Or do you run multiple instances or Rhino users in parallel? It clearly doesn’t let you remove something from a location which is supposed to be deletable. So either the file is in use, or you simply don’t have the right to delete there for whatever weird reason. But often its a symptom. And other things like Antivirus are causing it.
I think its worth to consider not trying to install so many dependencies for scripting purposes. Instead create dedicated applications or compiled plugins. Because it is really bad practice to run entire Python applications with n-dependencies over scripts.

When these errors occur, are you running as a user with the necessary permissions for this file?

The Python installation isn’t corrupted, but the env might be broken. Installing the deps into a venv instead and activating that venv in your script should preserve the normal default ‘system’ env.

The error above occurs when pip tries to install matplotlib. Something is trying to delete a folder tree:

  File "C:\Users\timoharboe-zollner\.rhinocode\py39-rh8\lib\shutil.py", line 749, in rmtree
    return _rmtree_unsafe(path, onerror)

It would be good to understand why the thing that tries to delete this folder, needs to do so (no surprise, it involves numpy). If you agree with it, that this is a good idea, e.g. if matplotlib is installing a more up to date version of numpy.linalg (it looks like a sub folder of the Rhino C Python 'system env, which rhino will recreate if it’s deleted anyway) or if you feel you know what you’re doing and deleting the folder is harmless, then a possible fix may be to simply delete that ...\numpy\linalg folder manually.

What I’m nervous about is when a user runs this. So there is no chance that I can edit anything manually. The venv may be a good solution.

I would recommend a venv in case the user is using the ‘system’ CPython environment. Doesn’t it just need a #env: env_name ?

Every plug-in sharing the same Python environment (and some of them doing what ever the heck they wanted with it) was not an endearing feature of the previous IronPython components.

If a user needs to install/download various dependencies, it’s a matter of (short) time until you break it. Either because you cause dependency incompatibility, version mismatches (changed API calls), or because you force the user to install deprecated packages. If you plan to distribute code to users, you should provide pre-build and bundled dependencies. In CPython you could use tools like cxFreeze for that. A venv is for developing purposes, and doesn’t solve packaging issues. Again, in my opinion, the problem is just a consequence of a weird software architecture .

Yes in an ideal world. But now we are talking Grasshopper scripts.

You are right, I’m also not saying that you did all of this. But you introduce more issues if you push the original scripting indent further. See normally I would agree to all of this. I basically use venv’s all the time when working with Python. But Grasshopper Scripting is special. Rhino is embedding a (deprecated) CPython installation for you. A venv means you copy a python distribution. Meaning that the Python version provided can already be seen as a venv. What you create in this case is essentially a venv in a venv if you will. Now imagine you do this on a GH definition based context. Each definition gets a different venv. Then let’s hope a user does not create more than 10 scripts with your tooling. This is not trivial. I’m not sure if this is something McNeel had on the radar, because you could argue that it’s already outside of the scripting boundary. In GH C# scripting world, there you hardly see scripts using an additional dependencies. Whenever you required this, it was clear sign to write a plugin. Since currently there is no good solution for Python, you could either switch to the traditional way of doing it (C# plugins) or you develop a non-monolithic approach, and only use the scripts to communicate with a dedicated Python application(-s) using interprocess communication.