Description
Environment data
- debugpy version: 1.8.14
- OS and version: Fedora 42
- Python version (& distribution if applicable, e.g. Anaconda): CPython 3.11.13
- Using VS Code or Visual Studio: VS Code
Actual behavior
Due to a complicated debugging environment I am trying to setup a launch configuration which spawns a Python process which in turn setups up an interactive terminal where new processes spawned in that terminal attaches to the VSCode client running the original launch request. For example I have this launch configuration
{
"version": "0.2.0",
"configurations": [
{
"name": "Python Debugger: Current File",
"type": "debugpy",
"request": "launch",
"program": "debugpy_test.py",
"console": "integratedTerminal",
"logToFile": true
},
]
}
debugpy_test.py
is the following:
import os
import subprocess
from debugpy.server import cli as debugpy_cli
def main() -> None:
host, addr = debugpy_cli.options.address
access_token = debugpy_cli.options.adapter_access_token
with open('child_test.py', 'w') as f:
f.write("import os\nimport debugpy\n\nprint(f'PID={os.getpid()} - debugpy.is_client_connected={debugpy.is_client_connected()}')\na = 1\nb = ''")
print(f"Launched PID {os.getpid()}\nSet breakpoint on line 5 and run\npython -m debugpy --connect {host}:{addr} --adapter-access-token {access_token} --log-to-stderr --wait-for-client child_test.py")
subprocess.run(
['bash'],
env=os.environ | {
'DEBUGPY_RUNNING': 'false',
'PYDEVD_DISABLE_FILE_VALIDATION': '1',
})
if __name__ == '__main__':
main()
When running it will print out a message like the following
Launched PID 103924
Set breakpoint on line 5 and run
python -m debugpy --connect 127.0.0.1:50865 --adapter-access-token ... --log-to-stderr --wait-for-client child_test.py
The child_test.py
is created by debugpy_test.py
with the following content
import os
import debugpy
print(f'PID={os.getpid()} - debugpy.is_client_connected={debugpy.is_client_connected()}')
a = 1
b = ''
In VSCode I set a breakpoint in child_test.py
and run echo $$
to get the bash PID and then run the printed Python command to launch child_test.py
through debugpy
connecting back to the original socket of the launch request.
The logs from stderr show that it connects and sets the pydevd.settrace
after connecting back to the socket.
I+00000.003: Also logging to "/home/jborean/.vscode-server/extensions/ms-python.debugpy-2025.8.0-linux-arm64/debugpy.server-104098.log".
I+00000.018: Initial environment:
System paths:
sys.executable: /home/jborean/dev/ansible/.venv/bin/python(/home/jborean/.local/share/uv/python/cpython-3.11.13-linux-aarch64-gnu/bin/python3.11)
sys.prefix: /home/jborean/dev/ansible/.venv
sys.base_prefix: /home/jborean/.local/share/uv/python/cpython-3.11.13-linux-aarch64-gnu
sys.real_prefix: <missing>
site.getsitepackages(): /home/jborean/dev/ansible/.venv/lib/python3.11/site-packages
site.getusersitepackages(): /home/jborean/.local/lib/python3.11/site-packages
sys.path (site-packages): /home/jborean/dev/ansible/.venv/lib/python3.11/site-packages
sysconfig.get_path('stdlib'): /home/jborean/.local/share/uv/python/cpython-3.11.13-linux-aarch64-gnu/lib/python3.11
sysconfig.get_path('platstdlib'): /home/jborean/dev/ansible/.venv/lib/python3.11
sysconfig.get_path('purelib'): /home/jborean/dev/ansible/.venv/lib/python3.11/site-packages
sysconfig.get_path('platlib'): /home/jborean/dev/ansible/.venv/lib/python3.11/site-packages
sysconfig.get_path('include'): /home/jborean/.local/share/uv/python/cpython-3.11.13-linux-aarch64-gnu/include/python3.11
sysconfig.get_path('scripts'): /home/jborean/dev/ansible/.venv/bin
sysconfig.get_path('data'): /home/jborean/dev/ansible/.venv
os.__file__: /home/jborean/.local/share/uv/python/cpython-3.11.13-linux-aarch64-gnu/lib/python3.11/os.py
threading.__file__: /home/jborean/.local/share/uv/python/cpython-3.11.13-linux-aarch64-gnu/lib/python3.11/threading.py
debugpy.__file__: /home/jborean/dev/ansible/.venv/lib/python3.11/site-packages/debugpy/__init__.py
Installed packages:
...
debugpy==1.8.14
I+00000.018: sys.argv before parsing: ['/home/jborean/dev/ansible/.venv/lib/python3.11/site-packages/debugpy/__main__.py', '--connect', '127.0.0.1:50865', '--adapter-access-token', 'a9c8d7e399cf091be4b665cab7001020f631369833f9e84bcf1856bedcfc1c6b', '--log-to-stderr', '--wait-for-client', 'child_test.py']
after parsing: ['/home/jborean/dev/ansible/.venv/lib/python3.11/site-packages/debugpy/__main__.py']
D+00000.018: sys.argv after patching: ['child_test.py']
D+00000.018: configure({'qt': 'none', 'subProcess': True}, {})
D+00000.018: connect(('127.0.0.1', 50865), **{'access_token': 'a9c8d7e399cf091be4b665cab7001020f631369833f9e84bcf1856bedcfc1c6b'})
I+00000.019: Initial debug configuration: {
"qt": "none",
"subProcess": true,
"python": "/home/jborean/dev/ansible/.venv/bin/python",
"pythonEnv": {}
}
D+00000.019: pydevd.settrace(*(), **{'host': '127.0.0.1', 'port': 50865, 'client_access_token': 'a9c8d7e399cf091be4b665cab7001020f631369833f9e84bcf1856bedcfc1c6b', 'suspend': False, 'patch_multiprocessing': True, 'dont_trace_start_patterns': ('/home/jborean/dev/ansible/.venv/lib/python3.11/site-packages/debugpy',), 'dont_trace_end_patterns': ('debugpy_launcher.py',)})
D+00000.172: wait_for_client()
I+00000.208: Pre-launch environment:
System paths:
sys.executable: /home/jborean/dev/ansible/.venv/bin/python(/home/jborean/.local/share/uv/python/cpython-3.11.13-linux-aarch64-gnu/bin/python3.11)
sys.prefix: /home/jborean/dev/ansible/.venv
sys.base_prefix: /home/jborean/.local/share/uv/python/cpython-3.11.13-linux-aarch64-gnu
sys.real_prefix: <missing>
site.getsitepackages(): /home/jborean/dev/ansible/.venv/lib/python3.11/site-packages
site.getusersitepackages(): /home/jborean/.local/lib/python3.11/site-packages
sys.path (site-packages): /home/jborean/dev/ansible/.venv/lib/python3.11/site-packages
sysconfig.get_path('stdlib'): /home/jborean/.local/share/uv/python/cpython-3.11.13-linux-aarch64-gnu/lib/python3.11
sysconfig.get_path('platstdlib'): /home/jborean/dev/ansible/.venv/lib/python3.11
sysconfig.get_path('purelib'): /home/jborean/dev/ansible/.venv/lib/python3.11/site-packages
sysconfig.get_path('platlib'): /home/jborean/dev/ansible/.venv/lib/python3.11/site-packages
sysconfig.get_path('include'): /home/jborean/.local/share/uv/python/cpython-3.11.13-linux-aarch64-gnu/include/python3.11
sysconfig.get_path('scripts'): /home/jborean/dev/ansible/.venv/bin
sysconfig.get_path('data'): /home/jborean/dev/ansible/.venv
os.__file__: /home/jborean/.local/share/uv/python/cpython-3.11.13-linux-aarch64-gnu/lib/python3.11/os.py
threading.__file__: /home/jborean/.local/share/uv/python/cpython-3.11.13-linux-aarch64-gnu/lib/python3.11/threading.py
debugpy.__file__: /home/jborean/dev/ansible/.venv/lib/python3.11/site-packages/debugpy/__init__.py
Installed packages:
...
debugpy==1.8.14
I+00000.208: Running file 'child_test.py'
PID=104098 - debugpy.is_client_connected=False
Unfortunately the VSCode client does not receive the breakpoint event and the script just completes.
When looking at the logs I see the following:
debugger.vscode_{guid}.log
- contains the exchange between VSCode the launcheddebugpy_test.py
from the launch configuration and nothing aboutchild_test.py
debugpy.adapter.{x}.log
- I see the VSCode client connection and breakpoint message exhanges
- I also see things like child_test.py
Accepted incoming Server connection from 127.0.0.1:xxxx
- This new connection does the negotiation exchange but I see things like
No active debug session for parent process of Server[pid=x]
andNo clients to wait for - unblocking Server[pid=x]
- It then sends the
configurationDone
request with no breakpoint - This tells me the sub process connects back to the VSCode side but it isn't detecting it should be part of the VSCode client that did the original launch
I'm assuming what is happening is that the adapter is getting the parent PID of child_test.py
and finding it is the bash process and not debugpy_test.py
that is the originally launched Python script. Because of this it just ignores the connection and continues on as normal.
If that is the case is there any way I can get this working while still including that interactive prompt to allow people to run whatever scenario they need under the debug context?
Expected behavior
The child process to be debuggable like normal sub processes.
Steps to reproduce:
See Actual behaviour