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 — gh-159303: Run asyncio's default exception handler in the task context by drakeo338 · Pull Request #159307 · python/cpython

Conversation

@drakeo338

@drakeo338 drakeo338 commented Oct 13, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

Fixes gh-159303.

With no custom handler set, call_exception_handler() called default_exception_handler() in the caller's context. gh-96704 made a custom handler run in the task's or handle's context, and its PR left the default handler for later. This moves the lookup into a helper and uses it for both.

Both handlers run in ctx.copy(). Without the copy, reporting from inside the task itself ('task': asyncio.current_task()) raises RuntimeError: cannot enter context, and a custom handler is silently skipped today.

Three tests in test_base_events:

  • the first fails on main (the default handler sees the caller's context);
  • the third fails on main (the custom handler is skipped);
  • the second passes on main and fails only if the copy is dropped.

The docs note both changes. The issue's repro now prints req-1 twice. ./python -m test --fast-ci -j8 test_asyncio passes.

Prepared with AI assistance (Claude), then reviewed and tested before submission.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@drakeo338
pythongh-159303: Run asyncio's default exception handler in the task …
c357cc2
…context
@drakeo338
drakeo338 requested review from 1st1, asvetlov, kumaraditya303 and willingc as code owners October 13, 2026 11:13
@bedevere-app bedevere-app Bot mentioned this pull request Oct 13, 2026
asyncio: default exception handler does not run in the task's context #159303
Open
@bedevere-app bedevere-app Bot added the awaiting review label Oct 13, 2026
@drakeo338
pythongh-159303: Document the exception handler context change
a6373a7
@read-the-docs-community

read-the-docs-community Bot commented Oct 13, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

Documentation build overview

📚 cpython-previews | 🛠️ Build #35509497 | 📁 Comparing 340c4e6 against main (50082b8)

  🔍 Preview build  

166 files changed · + 1 added · ± 165 modified

+ Added

  • c-api/pyexpat.html

± Modified

  • glossary.html
  • index.html
  • license.html
  • py-modindex.html
  • builtins/functions.html
  • builtins/stdtypes.html
  • c-api/bool.html
  • c-api/buffer.html
  • c-api/bytes.html
  • c-api/capsule.html
  • and 155 more...
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

OsmnvAslan
OsmnvAslan reviewed Oct 26, 2026
View reviewed changes

@OsmnvAslan OsmnvAslan left a comment

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked this out locally: test_base_events passes, and the two new tests fail on main as described. The move into _get_exception_context() is a straight refactor of the existing lookup.

One behaviour change worth documenting: a custom handler used to run directly in the task's context, so a ContextVar.set() inside the handler was visible to the task afterwards. With ctx.copy() that write is now discarded (I confirmed both behaviours with a small script on main and on this branch). I think that's the right call, but a sentence in the versionchanged note would avoid surprising anyone relying on it.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@drakeo338
pythongh-159303: Note that a handler's ContextVar writes are not visi…
340c4e6
…ble to the task
@drakeo338

drakeo338 commented Oct 29, 2026

Copy link
Copy Markdown
Author

Thanks for checking it locally. Added in 340c4e6: the versionchanged note now says that a ContextVar.set() made inside the handler is no longer visible to the task afterwards, since the handler runs in a copy of the context.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

asvetlov
asvetlov reviewed Oct 30, 2026
View reviewed changes
Comment thread Doc/library/asyncio-eventloop.rst
Comment on lines +1506 to +1508
The handler now runs in a copy of that context. Previously, reporting
an exception from inside the task's own context raised
:exc:`RuntimeError` and the handler was skipped. Because the handler

@asvetlov asvetlov Oct 30, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small accuracy nit: on main the handler isn't really skipped silently. The RuntimeError from ctx.run() is caught and passed to default_exception_handler() as "Unhandled error in exception handler", with the original context nested under 'context'. Maybe something like:

      The handler now runs in a copy of that context. Previously, reporting
      an exception from inside the task's own context failed with
      :exc:`RuntimeError`, and the handler was not called; the failure was
      logged as an unhandled error in the exception handler instead.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread Doc/library/asyncio-eventloop.rst
Comment on lines +1530 to +1535
.. versionchanged:: next

When no exception handler is set, :meth:`call_exception_handler`
runs this method in a copy of the :class:`contextvars.Context` of
the task or handle where the exception originated, as for a custom
handler.

@asvetlov asvetlov Oct 30, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This note describes how call_exception_handler() behaves rather than default_exception_handler() itself (calling loop.default_exception_handler() directly is unaffected). Consider moving it under loop.call_exception_handler instead, e.g. "When no custom handler is set, the default handler now runs in a copy of the context of the task or handle where the exception originated."

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread Lib/test/test_asyncio/test_base_events.py
self.loop.run_until_complete, self.loop.subprocess_shell,
asyncio.SubprocessProtocol, 'exit 0', bufsize=4096)

def test_default_exc_handler_runs_in_task_context(self):

@asvetlov asvetlov Oct 30, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This covers the 'task' and 'handle' keys, but not 'future', which is the path the issue's repro actually goes through: "Task exception was never retrieved" is reported from the task finalizer with 'future': task. Could you add that case here too, e.g.

            self.loop.call_exception_handler({
                'message': 'msg', 'future': task, 'exception': ValueError()})

(and extend the expected list accordingly). An end-to-end variant along the lines of the issue's repro (a failing task dropped without retrieving its exception, then support.gc_collect()) would also be nice, but the direct call is enough.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@asvetlov asvetlov asvetlov left review comments
@1st1 1st1 Awaiting requested review from 1st1 1st1 is a code owner
@kumaraditya303 kumaraditya303 Awaiting requested review from kumaraditya303 kumaraditya303 is a code owner
@willingc willingc Awaiting requested review from willingc willingc is a code owner
+1 more reviewer
@OsmnvAslan OsmnvAslan OsmnvAslan left review comments
Reviewers whose approvals may not affect merge requirements

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

awaiting review

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

asyncio: default exception handler does not run in the task's context

Uh oh!

There was an error while loading. Please reload this page.

3 participants

@drakeo338 @asvetlov @OsmnvAslan
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.