how to make socket server propagate exceptions up to TaskGroup where they're started from?
08:54 19 Nov 2025

How to make exceptions thrown from a socket server running in a task group task be propagated up to its parent TaskGroup? With given example, I'd expect to see the error raised from process_client_command() be caught by the except block in run_server(), but instead the server continues running and "Unhandled exception" is logged out.

First start server:

$ ./script.py server
DEBUG:asyncio:Using selector: EpollSelector

#  following is printed upon receiving data from client:
DEBUG:__main__:received task from client from client, raising exception...
ERROR:asyncio:Unhandled exception in client_connected_cb
transport: <_SelectorSocketTransport fd=7 read=idle write=>
Traceback (most recent call last):
  File "/tmp/script.py", line 19, in process_client_command
    raise RuntimeError("error processing client command")
RuntimeError: error processing client command

And from another terminal run client:

$ ./script.py client

Example itself:

#!/usr/bin/env python3

import asyncio
import sys
import logging
from logging import Logger

LOGGER: Logger = logging.getLogger(__name__)
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)

SOCK_PATH = "/tmp/test.sock"

async def process_client_commands() -> None:
    async def process_client_command(reader, writer):
        data = (await reader.read()).decode()
        LOGGER.debug(f"received task {data} from client, raising exception...")
        raise RuntimeError("error processing client command")
    server = await asyncio.start_unix_server(
        process_client_command, SOCK_PATH
    )
    await server.serve_forever()


async def run_server() -> None:
    try:
        async with asyncio.TaskGroup() as tg:
            tg.create_task(process_client_commands())
    except* Exception as exc_group:
        LOGGER.debug(f"{len(exc_group.exceptions)} errs caught in exc group")
        sys.exit(1)


async def _open_write_socket(msg: str):
    try:
        _, writer = await asyncio.open_unix_connection(SOCK_PATH)
    except FileNotFoundError:
        LOGGER.error("server is not running")
        sys.exit(1)
    LOGGER.debug(f"sending message {msg}")
    writer.write(msg.encode())
    await writer.drain()
    writer.write_eof()
    return writer


async def send_msg(msg: str):
    writer = await _open_write_socket(msg)
    LOGGER.debug("closing the connection")
    writer.close()
    await writer.wait_closed()


if len(sys.argv) <= 1:
    sys.exit(1)

arg = sys.argv[1]
if arg == "server":
    asyncio.run(run_server())
else:
    asyncio.run(send_msg("from client"))

python python-3.x exception error-handling python-asyncio