Skip to content

Make saltnado EventListener._handle_event_socket_recv a coroutine (#66177)#69476

Open
dwoz wants to merge 2 commits into
saltstack:3007.xfrom
dwoz:fix/issue-66177
Open

Make saltnado EventListener._handle_event_socket_recv a coroutine (#66177)#69476
dwoz wants to merge 2 commits into
saltstack:3007.xfrom
dwoz:fix/issue-66177

Conversation

@dwoz

@dwoz dwoz commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Makes salt.netapi.rest_tornado.saltnado.EventListener._handle_event_socket_recv an async coroutine so the TCP-based publish IPC client can schedule it with asyncio.create_task(callback(msg)) without raising TypeError.

What issues does this PR fix or reference?

Fixes #66177

Previous Behavior

salt-api and salt-master logs were flooded with one of the following exceptions for every event delivered through the TCP IPC publish path (any time EventListener is in use, e.g. salt-api with external_auth):

[ERROR   ] Unhandled exception while running callback <salt.transport.tcp.PublishClient object at ...>
Traceback (most recent call last):
  File ".../salt/transport/tcp.py", line 437, in on_recv_handler
    tasks.append(asyncio.create_task(callback(msg)))
  File ".../asyncio/tasks.py", line 337, in create_task
    task = loop.create_task(coro)
  File ".../asyncio/base_events.py", line 438, in create_task
    task = tasks.Task(coro, loop=self, name=name)
TypeError: a coroutine was expected, got None

(Earlier 3007.x reported the equivalent TypeError: object NoneType can't be used in 'await' expression.)

Each error meant the corresponding event was silently dropped, leaving EventListener futures unresolved and salt-api requests hanging until their timeouts.

New Behavior

_handle_event_socket_recv is now an async function. asyncio.create_task(callback(msg)) receives a coroutine, the event is dispatched normally, the log spam stops, and salt-api requests complete on time.

The body of the function is unchanged - no await is needed inside it; the fix is purely about returning a coroutine so the transport layer's asyncio.create_task contract is satisfied.

A regression test (tests/pytests/unit/netapi/saltnado/test_event_listener.py) asserts the callback is a coroutine function and that the returned object is awaitable, so a future drive-by edit cannot turn it back into a sync function without CI catching it.

Merge requirements satisfied?

  • Docs (no documented behavior changes)
  • Changelog (changelog/66177.fixed.md)
  • Tests written/updated (tests/pytests/unit/netapi/saltnado/test_event_listener.py)

Commits signed with GPG?

No (matches surrounding non-merge commits on this branch).

The salt-api EventListener registers _handle_event_socket_recv as the
on_recv callback for the publish IPC client. Since 3007 the TCP-based
publish client's on_recv_handler schedules the callback via
asyncio.create_task(callback(msg)). A plain (sync) function returning
None makes asyncio.create_task raise "TypeError: a coroutine was
expected, got None" (older Python versions reported the equivalent
"object NoneType can't be used in 'await' expression"), flooding
salt-api / salt-master logs and silently dropping events.

Defining _handle_event_socket_recv as an async function lets
asyncio.create_task wrap it cleanly. The function body is unchanged --
no await is needed inside it; the change is purely about returning a
coroutine to satisfy the transport's contract.

A regression test asserts the callback is a coroutine function and
that the returned object is awaitable (the operation the TCP publish
client performs), so a future drive-by edit cannot turn it back into a
sync function without CI catching it.

Fixes saltstack#66177
@dwoz dwoz requested a review from a team as a code owner June 18, 2026 05:56
@dwoz dwoz added this to the Chlorine v3007.15 milestone Jun 18, 2026
@dwoz dwoz added the test:full Run the full test suite label Jun 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test:full Run the full test suite

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant