pika/pika · error · TypeError

on_done arg must be callable, but got {on_done!r}

Error message

on_done arg must be callable, but got {on_done!r}

What it means

`_AsyncioIOServicesAdapter._schedule_and_wrap_in_io_ref` wraps an asyncio future/coroutine and registers an `on_done` callback for completion. The guard rejects a non-callable `on_done` immediately so the future is never left without a completion handler. This method is reached through the public `getaddrinfo` method (which takes `on_done`) and through streaming-connection setup.

Source

Thrown at pika/adapters/asyncio_connection.py:259

        :param fd: File descriptor
        """
        LOGGER.debug('remove_writer(%s)', fd)
        return self._loop.remove_writer(fd)

    def _schedule_and_wrap_in_io_ref(
        self, coro: Awaitable[Any],
        on_done: Callable[[base_connection.BaseConnection | BaseException],
                          None]
    ) -> _AsyncioIOReference:
        """
        Schedule the coroutine to run and return _AsyncioIOReference.

        :param coro: Coroutine to schedule.
        :param on_done: User callback that takes the completion result or exception as its only arg.
            It will not be called if the operation was cancelled.
        """
        if not callable(on_done):
            raise TypeError(
                f'on_done arg must be callable, but got {on_done!r}')

        return _AsyncioIOReference(asyncio.ensure_future(coro, loop=self._loop),
                                   on_done)


class _TimerHandle(nbio_interface.AbstractTimerReference):
    """This module's adaptation of `nbio_interface.AbstractTimerReference`."""

    def __init__(self, handle: asyncio.Handle) -> None:
        """

        :param handle:
        """
        self._handle: asyncio.Handle | None = handle

    @override
    def cancel(self) -> None:

View on GitHub (pinned to 34a407b24f)

Solutions

  1. Pass a real callable (function/lambda/bound method) as `on_done`.
  2. If you don't need completion notification, pass a no-op: `on_done=lambda *_: None`.

Example fix

# before
nbio.getaddrinfo(host, port, on_done=None)
# after
nbio.getaddrinfo(host, port, on_done=lambda addrinfo_or_exc: None)
Defensive patterns

Strategy: type-guard

Validate before calling

if not callable(on_done):
    raise TypeError('on_done must be callable')
nbio.getaddrinfo(host, port, on_done=on_done)

Type guard

def is_callable_zero_arg(fn) -> bool:
    return callable(fn)

Prevention

When it happens

Trigger: Calling `nbio.getaddrinfo(host, port, on_done=<non-callable>)` on the asyncio nbio adapter, or subclassing `_AsyncioIOServicesAdapter` / implementing a custom transport that forwards a None or non-callable completion callback into this method.

Common situations: Implementing a custom `AbstractStreamTransport` or `AbstractFileDescriptorIOServices` and forgetting to pass the completion callback, or passing a string attribute name or a result object instead of a function for `on_done`.

Related errors


AI-assisted analysis of pika/pika@34a407b24f (2026-08-07). Data as JSON: /api/errors/8a5ee3b8237951d4. Report an issue: GitHub.