Skip to content

pre-commit failures on Python 3.14 #15525

@brianschubert

Description

@brianschubert

For the last few days, pre-commit CI has been failing with the below error:

pre-commit output
black....................................................................Failed
- hook id: black
- exit code: 1

All done! ✨ 🍰 ✨
2031 files left unchanged.

flake8...................................................................Failed
- hook id: flake8
- exit code: 1

Traceback (most recent call last):
  File "/pc/clone/46XTqQO9RBO8GQKMBaFx0Q/py_env-python3.14/bin/flake8", line 7, in <module>
    sys.exit(main())
             ~~~~^^
  File "/pc/clone/46XTqQO9RBO8GQKMBaFx0Q/py_env-python3.14/lib/python3.14/site-packages/flake8/main/cli.py", line 23, in main
    app.run(argv)
    ~~~~~~~^^^^^^
  File "/pc/clone/46XTqQO9RBO8GQKMBaFx0Q/py_env-python3.14/lib/python3.14/site-packages/flake8/main/application.py", line 198, in run
    self._run(argv)
    ~~~~~~~~~^^^^^^
  File "/pc/clone/46XTqQO9RBO8GQKMBaFx0Q/py_env-python3.14/lib/python3.14/site-packages/flake8/main/application.py", line 187, in _run
    self.run_checks()
    ~~~~~~~~~~~~~~~^^
  File "/pc/clone/46XTqQO9RBO8GQKMBaFx0Q/py_env-python3.14/lib/python3.14/site-packages/flake8/main/application.py", line 103, in run_checks
    self.file_checker_manager.run()
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
  File "/pc/clone/46XTqQO9RBO8GQKMBaFx0Q/py_env-python3.14/lib/python3.14/site-packages/flake8/checker.py", line 235, in run
    self.run_parallel()
    ~~~~~~~~~~~~~~~~~^^
  File "/pc/clone/46XTqQO9RBO8GQKMBaFx0Q/py_env-python3.14/lib/python3.14/site-packages/flake8/checker.py", line 196, in run_parallel
    pool = _try_initialize_processpool(self.jobs, self.argv)
  File "/pc/clone/46XTqQO9RBO8GQKMBaFx0Q/py_env-python3.14/lib/python3.14/site-packages/flake8/checker.py", line 591, in _try_initialize_processpool
    return multiprocessing.Pool(job_count, _mp_init, initargs=(argv,))
           ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.14/multiprocessing/context.py", line 119, in Pool
    return Pool(processes, initializer, initargs, maxtasksperchild,
                context=self.get_context())
  File "/usr/lib/python3.14/multiprocessing/pool.py", line 215, in __init__
    self._repopulate_pool()
    ~~~~~~~~~~~~~~~~~~~~~^^
  File "/usr/lib/python3.14/multiprocessing/pool.py", line 306, in _repopulate_pool
    return self._repopulate_pool_static(self._ctx, self.Process,
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^
                                        self._processes,
                                        ^^^^^^^^^^^^^^^^
    ...<3 lines>...
                                        self._maxtasksperchild,
                                        ^^^^^^^^^^^^^^^^^^^^^^^
                                        self._wrap_exception)
                                        ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.14/multiprocessing/pool.py", line 329, in _repopulate_pool_static
    w.start()
    ~~~~~~~^^
  File "/usr/lib/python3.14/multiprocessing/process.py", line 121, in start
    self._popen = self._Popen(self)
                  ~~~~~~~~~~~^^^^^^
  File "/usr/lib/python3.14/multiprocessing/context.py", line 300, in _Popen
    return Popen(process_obj)
  File "/usr/lib/python3.14/multiprocessing/popen_forkserver.py", line 35, in __init__
    super().__init__(process_obj)
    ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
  File "/usr/lib/python3.14/multiprocessing/popen_fork.py", line 20, in __init__
    self._launch(process_obj)
    ~~~~~~~~~~~~^^^^^^^^^^^^^
  File "/usr/lib/python3.14/multiprocessing/popen_forkserver.py", line 51, in _launch
    self.sentinel, w = forkserver.connect_to_new_process(self._fds)
                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
  File "/usr/lib/python3.14/multiprocessing/forkserver.py", line 89, in connect_to_new_process
    self.ensure_running()
    ~~~~~~~~~~~~~~~~~~~^^
  File "/usr/lib/python3.14/multiprocessing/forkserver.py", line 190, in ensure_running
    os.write(authkey_w, self._forkserver_authkey)
    ~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
BrokenPipeError: [Errno 32] Broken pipe

It seems that this started when pre-commit CI upgraded to using Python 3.14 for the hook venvs:

  • Last successful run: link (note: env: python@python3.12 under build)
  • First failing run: link (note: env: python@python3.14 under build)

I can also reproduce this locally when running pre-commit using Python 3.14.

Some miscellaneous observations I've made so far:

  1. The failures reproduce when using prek instead of pre-commit (so this is likely not a bug in pre-commit or pre-commit.ci)
  2. The hooks pass if I disable black/flake8's internal multiprocessing (this makes me wonder if this is somehow related to the default start method changing from fork to forkserver in Python 3.14)
  3. The hooks only fail when passing lots of filenames on the command line.
    • The hooks pass if I delete a bunch of random stub files via
      git ls-files '*.pyi' | shuf | head -n2500 | xargs git rm --cached
      
      Likely related: this reduces the number of times pre-commit internally invokes black/flake8 with batches of filenames from 2 to 1.
    • The hooks pass if I set pass_filenames: false and pass the relevant source directories to args:

Based on the above, some possible solutions / bandaids include:

  1. Pin the hooks to Python <3.14 (draw back: all contributors running these hooks locally would need to have this version of Python installed. pre-commit only supports pinning to a specific version, not a range)
  2. Disable black/flake8's internal multiprocessing (draw back: slow)
  3. Use pass_filenames: false and have black/flake8 always process all files (draw back: slow when committing locally)

Metadata

Metadata

Assignees

No one assigned

    Labels

    project: infrastructuretypeshed build, test, documentation, or distribution related

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions