3

Problem

I'm encountering an issue where launching PowerShell via Python’s subprocess.Popen() works as expected during normal execution, but in debug mode (using debugpy/cursor) key environment variables (e.g. PROGRAMFILES and LOCALAPPDATA) are empty. In contrast, when I run a CMD command (e.g. echo %PROGRAMFILES%), the environment variables are correctly inherited.

Environment

  • Windows 22H2 build 19045.5487
  • Python 3.10.16

What I've Tested (V1)

I created a small test program with three functions: one for PowerShell with the -NoProfile option (print_psh), one for PowerShell without that option (print_psh_with_profile), and one for CMD (print_cmd). I also made a variant (print_psh_with_profile_inject_env) where I pass env=os.environ.copy() explicitly.

  • This is the version before @mklement0 mentioned about shell option.
  • The Write-Host problem isn't affected, so I'm keeping it in V1.
import subprocess
import os

def print_psh(cmd):
    with subprocess.Popen(
        "powershell -NoProfile " + '"' +
        f"$ErrorActionPreference='silentlycontinue'; $tmp = ({cmd}); if ($tmp){{echo $tmp; Exit;}}" + '"',
        stdout=subprocess.PIPE,
        stdin=subprocess.DEVNULL,
        stderr=subprocess.DEVNULL,
        shell=True, # Is the "powershell" expression recognized as a CMD.exe command? This returns nothing, but it works without error.(see the description about *V2* code)
    ) as stream:
        cmm = stream.communicate()  
        stdout = cmm[0].decode()
        print(f"NoProfile: {cmd} = {stdout}")

def print_psh_with_profile(cmd):
    with subprocess.Popen(
        "powershell " + '"' +
        f"$ErrorActionPreference='silentlycontinue'; $tmp = ({cmd}); if ($tmp){{echo $tmp; Exit;}}" + '"',
        stdout=subprocess.PIPE,
        stdin=subprocess.DEVNULL,
        stderr=subprocess.DEVNULL,
        shell=True, # Is the "powershell" expression recognized as a CMD.exe command? This returns nothing, but it works without error.(see the description about *V2* code)
    ) as stream:
        cmm = stream.communicate()  
        stdout = cmm[0].decode()
        print(f"WithProfile: {cmd} = {stdout}")

def print_psh_with_profile_inject_env(cmd):
    with subprocess.Popen(
        "powershell " + '"' +
        f"$ErrorActionPreference='silentlycontinue'; $tmp = ({cmd}); if ($tmp){{echo $tmp; Exit;}}" + '"',
        stdout=subprocess.PIPE,
        stdin=subprocess.DEVNULL,
        stderr=subprocess.DEVNULL,
        shell=True, # Is the "powershell" expression recognized as a CMD.exe command? This returns nothing, but it works without error.(see the description about *V2* code)
        env=os.environ.copy(),
    ) as stream:
        cmm = stream.communicate()  
        stdout = cmm[0].decode()
        print(f"WithProfile(inject env): {cmd} = {stdout}")

def print_cmd(cmd):
    with subprocess.Popen(
        cmd,
        stdout=subprocess.PIPE,
        stdin=subprocess.DEVNULL,
        stderr=subprocess.DEVNULL,
        shell=True, # use commandline commands.
    ) as stream:
        cmm = stream.communicate()  
        stdout = cmm[0].decode()
        print(f"CMD.EXE: {cmd} = {stdout}")

print_psh("$env:PROGRAMFILES")
print_psh("$env:LOCALAPPDATA")
print_cmd("echo %PROGRAMFILES%")
print_cmd("echo %LOCALAPPDATA%")
print_psh_with_profile("$env:PROGRAMFILES")
print_psh_with_profile("$env:LOCALAPPDATA")
print_psh_with_profile_inject_env("$env:PROGRAMFILES")
print_psh_with_profile_inject_env("$env:LOCALAPPDATA")

Output (V1)

Normal Execution (non-debug mode):

NoProfile: $env:PROGRAMFILES = C:\Program Files
NoProfile: $env:LOCALAPPDATA = C:\Users\(USERNAME)\AppData\Local
CMD.EXE: echo %PROGRAMFILES% = C:\Program Files
CMD.EXE: echo %LOCALAPPDATA% = C:\Users\(USERNAME)\AppData\Local
WithProfile: $env:PROGRAMFILES = [profile script output] ... C:\Program Files
WithProfile: $env:LOCALAPPDATA = [profile script output] ... C:\Users\(USERNAME)\AppData\Local
WithProfile(inject env): $env:PROGRAMFILES = C:\Program Files
WithProfile(inject env): $env:LOCALAPPDATA = C:\Users\(USERNAME)\AppData\Local

Debug Mode (using debugpy):

NoProfile: $env:PROGRAMFILES = 
NoProfile: $env:LOCALAPPDATA = 
CMD.EXE: echo %PROGRAMFILES% = C:\Program Files
CMD.EXE: echo %LOCALAPPDATA% = C:\Users\(USERNAME)\AppData\Local
WithProfile: $env:PROGRAMFILES = 
WithProfile: $env:LOCALAPPDATA = 
WithProfile(inject env): $env:PROGRAMFILES = 
WithProfile(inject env): $env:LOCALAPPDATA = 

What I've Tested (V2)

I changed the code more precise, then I obtained a new error and clue.

  • This is the version before @mklement0 mentioned about shell option.
  • Change shell option to False in all print_psh family because it might be secure that 'powershell' expression is not treat as CMD.EXE command.
...
def print_psh...(cmd):
    with subprocess.Popen(
        "powershell -NoProfile " + '"' +
        ...
        stderr=subprocess.DEVNULL,
-        shell=True, # Is the "powershell" expression recognized as a CMD.exe command? This returns nothing, but it works without error.(see the description about *V2* code)
+        shell=False, # PowerShell commands are indirectlly called from a new process.
    ) as stream:
...

Add this code on the beginning of the code V1.

+def print_psh_test():
+    cmd = "powershell"
+    with subprocess.Popen(
+        cmd,
+        stdout=subprocess.PIPE,
+        stdin=subprocess.DEVNULL,
+        stderr=subprocess.DEVNULL,
+        shell=False, # PowerShell commands are indirectlly called from +a new process.
+    ) as stream:
+        cmm = stream.communicate()
+        print("test to run powershell.")
...
+print_psh_test()
print_psh("$env:PROGRAMFILES")
print_psh("$env:LOCALAPPDATA")
...

Output (V2)

Normal Execution (non-debug mode):

(Same as V1)

Debug Mode (using debugpy):

<Error occurs: below stacktrace>
Exception has occurred: FileNotFoundError
[WinError 2] The system cannot find the file specified.
  File "C:\...\{source_file}.py", line 6, in print_psh_test
    with subprocess.Popen(
  File "C:\...\{source_file}.py", line 71, in <module>
    print_psh_test()
FileNotFoundError: [WinError 2] The system cannot find the file specified.
  • Test in the Miniconda Powershell Prompt.
    1. Activate the virtual environment.
    2. Run the command in the Prompt. Wait to attach manually.
python -m debugpy --listen 5678 --wait-for-client ./{source_file}.py.`
  1. Create the launch task (.vscode/launch.json)
...
        {
            "name": "Python: Attach",
            "type": "python",
            "request": "attach",
            "connect": {
              "host": "localhost",
              "port": 5678
            },
            "pathMappings": [
                {
                    "localRoot": "${workspaceFolder}",
                    "remoteRoot": "."
                }
            ],
            "justMyCode": true
        }
...
  1. Attach the pydebug from VSCode.

Debug Mode (using debugpy manually):

(Same as Normal Execution)

What I've Tried

  • (V1) Removing -NoProfile did not change the result in debug mode.
  • (V1) Passing env=os.environ.copy() explicitly also had no effect.
  • (V1) When using CMD (via echo %PROGRAMFILES%), the environment variables are correctly inherited.
  • New! (V2) Popen powershell with shell=False option will cause the FileNotFound error, although not with shell=Ture.
  • New! (V2) This could be due to any effect on VSCode's Integrated Terminal and pydebug combination, and it might be specific to my environment. (@Grismar tested it in the same enviornment, but did not reproduce the problem)

My Question

It appears that when running under debugpy (or the associated debug environment), the PowerShell subprocess is launched with an environment that lacks the expected variables (they are empty), while CMD subprocesses inherit the environment normally.

Has anyone encountered this behavior with debugpy or similar debuggers?

  • Is debugpy known to override or clear the environment when launching PowerShell subprocesses?
  • Are there any workarounds or debug configuration settings (e.g., in launch.json) to ensure that the full environment is passed to PowerShell even in debug mode?

Any insights or suggestions would be greatly appreciated.


Background and How I Got Here

This issue was discovered while trying to use webdriver_manager to automatically download and launch a compatible version of ChromeDriver in a script launched under debugpy. I noticed that webdriver_manager was failing to detect the installed Chrome version on my system. Upon closer inspection, I found that it internally attempts to retrieve the Chrome version using PowerShell commands (e.g., accessing registry keys or file paths that rely on environment variables like PROGRAMFILES).

To understand why this was failing only in debug mode, I traced the call stack and found that it eventually reaches a subprocess.Popen() call that runs PowerShell. This led me to test minimal reproducible examples using Popen directly, where I discovered that under debugpy, environment variables expected by PowerShell are inexplicably missing—while the same code behaves correctly outside of debug mode or when invoking CMD instead.

Hence, it seems that the root cause of webdriver_manager failing in debug mode stems from PowerShell being launched in an environment that lacks essential variables like PROGRAMFILES or LOCALAPPDATA.

Enviornment (Editor)

  • VSCode 1.96.2
  • Extensions (latest at 2025/4/16)
    • Python @2025.4.0
    • Python Debugger @2025.4.1
    • Pylance @2025.4.1
6
  • 1
    Two asides (I don't expect them to make a difference): You don't need Shell=True for your powershell.exe calls. Not only is use of Write-Host unnecessary, its output cannot be captured or redirected inside a PowerShell session (at least not by default).
    – mklement0
    Commented Apr 15 at 11:53
  • 1
    Running your code from VSCode on the same version of Windows, on the same version of Python, I see no difference between running it directly or while debugging (using debugpy). I don't use Cursor, so I can't say if that affects what you might be seeing, but the other elements of your environment don't seem like they would affect the outcome.
    – Grismar
    Commented Apr 15 at 12:21
  • 1
    @mklement0 Thanks for the feedback. I got rid of the "Write-Host" command in PowerShell because it messes up the "echo" code line. (return $null, so it makes a useless empty line). The "Shell" options have also been changed.
    – KLc3088
    Commented Apr 16 at 4:34
  • @Grismar Thank you for taking the time to look into this. I've tested it with VSCode(not Cursor) and manual pydebug, using the command "python -m debugpy --listen 5678 --wait-for-client .{source_file}.py." in the new and clean VSCode profile. I removed "cursor" from the environment. However, I'm still getting the same result in my environment. I'm looking into now.
    – KLc3088
    Commented Apr 16 at 4:40
  • I tried running exactly that, using the exact same version of Python and on the same Windows build (running in VSCode that's up to date), I see the correct output for all cases running that from the terminal. So, I'm sorry to say I feel your problem cannot be reproduced. I would recommend trying this yourself on another machine. If you can reproduce it there, perhaps update the instructions on how exactly to reproduce your issue.
    – Grismar
    Commented Apr 16 at 6:54

1 Answer 1

0

Self‑Answer: Root Cause and Fix

Apologies for the confusion and for taking up your time with this—it was my own mistake. I really appreciate your help.

After all the investigation above, I finally discovered the true culprit: a project‑root .env file that I had added for other tooling (e.g. to set JAVA_HOME for a Processing‑like Python library py5). That file contained something like:

# .env at the workspace root
JAVA_HOME=C:\Some\Java\Path
PATH=$JAVA_HOME$;$PATH

Why this caused the issue

  • Normal execution in VSCode’s Integrated Terminal sources .env and appends it to the existing environment, so your real PATH (and PROGRAMFILES, LOCALAPPDATA, etc.) remain intact—PowerShell and CMD subprocesses work fine.
  • Debug mode with debugpy, however, appears to load only the variables from .env and override the entire process environment. As a result:
    • PATH becomes exactly C:\Some\Java\Path\bin;$PATH (no Windows system paths)
    • powershell.exe can’t be found (or finds no inherited variables like PROGRAMFILES)
    • Any $env:… lookups return empty strings

The fix

Remove the PATH varable in the .env file.

# .env — extend the existing PATH instead of overwriting it
JAVA_HOME=C:\Some\Java\Path

PATH=%JAVA_HOME%;%PATH%

# Wrong. VARIABLES are not allowed in the .env files, only LITERALS.

It is difficult to simply do anything valid to the PATH variable with the .env files when it is in the VS Code integrated terminal using PowerShell, lunching Python in debug mode.

Follow-up Question

While the original question has been resolved, a deeper and more specific issue remains regarding how VS Code's debugger handles the PATH environment variable in certain cases.

I have posted a follow-up question here for further discussion and clarification:
🔗 Why does VS Code debugger reset PATH from .env, breaking subprocess behavior?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.