python/cpython · error · RuntimeError

Transport is closing

Error message

Transport is closing

What it means

A RuntimeError raised by loop.sendfile() when the transport passed in is already closing. sendfile enqueues bytes on a live transport; once is_closing() is true the transport will never write again (peer closed, protocol aborted, or transport.close() was called), so asyncio refuses with RuntimeError instead of silently dropping the file.

Source

Thrown at Lib/asyncio/base_events.py:1276

        file must be a regular file object opened in binary mode.

        offset tells from where to start reading the file. If specified,
        count is the total number of bytes to transmit as opposed to
        sending the file until EOF is reached. File position is updated on
        return or also in case of error in which case file.tell()
        can be used to figure out the number of bytes
        which were sent.

        fallback set to True makes asyncio to manually read and send
        the file when the platform does not support the sendfile syscall
        (e.g. Windows or SSL socket on Unix).

        Raise SendfileNotAvailableError if the system does not support
        sendfile syscall and fallback is False.
        """
        if transport.is_closing():
            raise RuntimeError("Transport is closing")
        mode = getattr(transport, '_sendfile_compatible',
                       constants._SendfileMode.UNSUPPORTED)
        if mode is constants._SendfileMode.UNSUPPORTED:
            raise RuntimeError(
                f"sendfile is not supported for transport {transport!r}")
        if mode is constants._SendfileMode.TRY_NATIVE:
            try:
                return await self._sendfile_native(transport, file,
                                                   offset, count)
            except exceptions.SendfileNotAvailableError:
                if not fallback:
                    raise

        if not fallback:
            raise exceptions.SendfileNotAvailableError(
                f"fallback is disabled and native sendfile is not "
                f"supported for transport {transport!r}")
        return await self._sendfile_fallback(transport, file,

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Track connection state in your protocol (set a flag in connection_lost) and skip/cancel pending sendfile tasks.
  2. Await the sendfile in a try/except RuntimeError and treat it as a normal disconnect, not a bug.
  3. Cancel outstanding sendfile tasks during shutdown before closing the transport.

Example fix

// before
async def stream_file(transport, path):
    with open(path, 'rb') as f:
        await loop.sendfile(transport, f)  # may race client disconnect

// after
async def stream_file(transport, path):
    with open(path, 'rb') as f:
        try:
            await loop.sendfile(transport, f)
        except RuntimeError:
            log('peer disconnected during transfer')
Defensive patterns

Strategy: try-catch

Validate before calling

if transport.is_closing():
    log.info('skipping sendfile: transport is closing')
    return 0

Try / catch

try:
    sent = await loop.sendfile(transport, f, offset, count)
except RuntimeError as e:
    if 'Transport is closing' not in str(e):
        raise
    log.info('peer disconnected during sendfile')
    sent = 0

Prevention

When it happens

Trigger: Awaiting loop.sendfile(transport, f, offset, count) after transport.close(), after connection_lost fired (peer reset), or in a protocol callback that runs while shutdown is in progress (e.g. inside ConnectionLost handling).

Common situations: Servers that stream files and race client disconnects: the client goes away, connection_lost fires, but a queued sendfile task still runs; graceful-shutdown handlers that try to flush a file through a closing transport.

Related errors


AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14). Data as JSON: /api/errors/06f12eb8c06f0cc8. Report an issue: GitHub.