Skip to content

asyncio.loop.sendfile(fallback=False) raises inconsistent exceptions when native sendfile is unavailable #152099

Closed

Description

@tarasko

Bug report

Bug description:

asyncio.loop.sendfile() does not reliably report native sendfile unavailability when fallback=False.

The documentation says:

Raise SendfileNotAvailableError if the system does not support the sendfile syscall and fallback is False.

However, the actual exception depends on the transport mode:

  • TRY_NATIVE transport where native sendfile fails:
    raises SendfileNotAvailableError
  • FALLBACK transport, such as SSL/TLS:
    raises RuntimeError("fallback is disabled and native sendfile is not supported ...")
  • unsupported transport:
    raises RuntimeError("sendfile is not supported ...")

This makes it hard for callers to reliably detect that native sendfile is unavailable. Catching RuntimeError is too broad, because it can also indicate unrelated states such as a closing transport or an unsupported
transport object.

For example, in aiohttp there is a project specific sendfile fallback implementation, which is more efficient than asyncio's fallback.
I did a small PR aio-libs/aiohttp#12945
which illustrates performance difference but there is no reliable way to detect that loop.sendfile can't be used with a particular transport. So I had to use private members from asyncio to do that.

It would be nice to just have the following logic:

try:
    loop.sendfile(transport, ..., fallback=False)
except asyncio.SendfileNotAvailableError:
     _sendfile_fallback(...)

But, it doesn't work for at least SSL transports.

Suggested fix:

Always raise SendfileNotAvailableError when native sendfile for transport can't be used and fallback=False

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Linked PRs

Activity

  1. zainnadeem786 commented on Jun 25, 2026

    @zainnadeem786
    Contributor

    I investigated this locally and was able to reproduce the inconsistency.

    The narrow case appears to be _SendfileMode.FALLBACK transports, such as SSL/TLS transports. These are valid sendfile-capable transports via fallback, but when fallback=False they currently raise a generic RuntimeError instead of asyncio.SendfileNotAvailableError.

    I agree that unsupported transports and closing transports should continue to raise RuntimeError, since those represent invalid transport/state cases rather than native sendfile unavailability.

    The minimal fix looks like changing only the fallback-only transport branch in BaseEventLoop.sendfile() to raise SendfileNotAvailableError, plus updating the existing asyncio sendfile test that currently expects RuntimeError.

    I can prepare a focused PR for this.

  2. tarasko commented on Jun 25, 2026

    @tarasko
    Author

    I investigated this locally and was able to reproduce the inconsistency.

    The narrow case appears to be _SendfileMode.FALLBACK transports, such as SSL/TLS transports. These are valid sendfile-capable transports via fallback, but when fallback=False they currently raise a generic RuntimeError instead of asyncio.SendfileNotAvailableError.

    I agree that unsupported transports and closing transports should continue to raise RuntimeError, since those represent invalid transport/state cases rather than native sendfile unavailability.

    The minimal fix looks like changing only the fallback-only transport branch in BaseEventLoop.sendfile() to raise SendfileNotAvailableError, plus updating the existing asyncio sendfile test that currently expects RuntimeError.

    I can prepare a focused PR for this.

    Thank you! Raising SendfileNotAvailableError exception for FALLBACK transports is definitely the most important case.

  3. zainnadeem786 commented on Jun 25, 2026

    @zainnadeem786
    Contributor

    Thanks for confirming. I opened a focused PR for the fallback-only transport case: gh-152223.

  4. added
    stdlibStandard Library Python modules in the Lib/ directory
    on Jun 27, 2026
  5. added a commit that references this issue on Jun 27, 2026
  6. added a commit that references this issue on Jun 28, 2026
  7. moved this from Todo to Done in asyncioon Jun 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    stdlibStandard Library Python modules in the Lib/ directorytopic-asynciotype-bugAn unexpected behavior, bug, or error

    Projects

    • Status
      Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions