GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — ThreadPoolExecutor marks interpreter as shutdown before non daemon threads exit · Issue #148280 · python/cpython

ThreadPoolExecutor marks interpreter as shutdown before non daemon threads exit #148280

New issue
Copy link
New issue
Copy link
Open
Open
ThreadPoolExecutor marks interpreter as shutdown before non daemon threads exit#148280
Copy link
Assignees
ZeroIntensity
Labels
3.15bugs and security fixesbugs and security fixesstdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytriagedThe issue has been accepted as valid by a triager.The issue has been accepted as valid by a triager.type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

Description

@aaix
aaix
opened on Apr 9, 2026
Issue body actions

Bug report

Bug description:

When the main thread exits, concurrent.futures.ThreadPoolExecutor mistakenly marks the interpreter as shutdown, regardless of if there are non daemon threads still executing, this causes any work submitted to the executor to fail with RuntimeError: cannot schedule new futures after interpreter shutdown.

PoC:

import time
import threading

from concurrent.futures import ThreadPoolExecutor

def work():
    print("Starting work")
    time.sleep(1)
    print("Work complete")

def worker_thread():
    # sleep a little to give the main thread time to exit
    time.sleep(1)

    executor = ThreadPoolExecutor()
    
    future = executor.submit(work)
    

def main():
    thread = threading.Thread(target=worker_thread)
    thread.start()

    # main thread exits now

if __name__ == "__main__":
    main()

Result

Exception in thread Thread-1 (worker_thread):
Traceback (most recent call last):
  File "/usr/lib/python3.14/threading.py", line 1081, in _bootstrap_inner
    self._context.run(self.run)
    ~~~~~~~~~~~~~~~~~^^^^^^^^^^
  File "/usr/lib/python3.14/threading.py", line 1023, in run
    self._target(*self._args, **self._kwargs)
    ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/*****/*****/implementation/./bug.py", line 18, in worker_thread
    future = executor.submit(work)
  File "/usr/lib/python3.14/concurrent/futures/thread.py", line 207, in submit
    raise RuntimeError('cannot schedule new futures after '
                       'interpreter shutdown')
RuntimeError: cannot schedule new futures after interpreter shutdown

Interpreter is marked as shutdown here:

cpython/Lib/concurrent/futures/thread.py

Lines 23 to 37 in f46a17b

def _python_exit():
global _shutdown
with _global_shutdown_lock:
_shutdown = True
items = list(_threads_queues.items())
for t, q in items:
q.put(None)
for t, q in items:
t.join()
# Register for `_python_exit()` to be called just before joining all
# non-daemon threads. This is used instead of `atexit.register()` for
# compatibility with subinterpreters, which no longer support daemon threads.
# See bpo-39812 for context.
threading._register_atexit(_python_exit)

Expected Result:
All non daemon threads should be able to use ThreadPoolExecutor until they exit.

example was tested on 3.14.0

cc @ZeroIntensity

CPython versions tested on:

3.14

Operating systems tested on:

Linux

Activity

  1. aaix
    added
    type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
    on Apr 9, 2026
  2. ZeroIntensity
    added
    stdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directory
    triagedThe issue has been accepted as valid by a triager.The issue has been accepted as valid by a triager.
    3.15bugs and security fixesbugs and security fixes
    on Apr 9, 2026
  3. ZeroIntensity commented on Apr 9, 2026

    @ZeroIntensity
    ZeroIntensity
    on Apr 9, 2026
    Member
    More actions

    In 3.15, we join non-daemon threads in a loop, so we should be able to safely create new futures even after threading._shutdown has been called for the first time. I'll be working on this.

  4. ZeroIntensity
    self-assigned this
    on Apr 9, 2026
  5. serhiy-storchaka
    added this to Multiprocessing issueson Jul 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

  • @ZeroIntensity
    ZeroIntensity

Labels

3.15bugs and security fixesbugs and security fixesstdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytriagedThe issue has been accepted as valid by a triager.The issue has been accepted as valid by a triager.type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

Projects

  • Multiprocessing issues
    Status
    No status
    Show more project fields

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions

You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.