Skip to content

Unsubscribing from inside a message handler hangs #180

Open

Description

@lalinsky

Releasing a subscription from within its own message handler never returns, and the program stops making progress.

fn handler(msg: *nats.Message, sub_ptr: **nats.Subscription) !void {
    if (std.mem.eql(u8, msg.data, "stop")) {
        sub_ptr.*.deinit(); // never returns
    }
    msg.deinit();
}

sub.deinit() waits for the subscription's handler task to finish before it returns, and here the call is being made from that very task, so it waits for itself. The handler never returns either, so Connection.deinit() afterwards blocks forever as well - it waits for every subscription to be released, and this one now never will be.

This is not specific to deinit(). Anything that destroys the subscription from within its handler hangs the same way: the memory being freed includes the task group the running handler belongs to.

Stopping a subscription in reaction to a message is a normal thing to want - a reply arrives, a sentinel value shows up, an error makes further messages pointless - and there is currently no supported way to do it from the handler. autoUnsubscribe covers only the fixed-count case, decided before any message arrives.

Subscription.deinit documents the restriction, so the current behaviour is intentional rather than an oversight, but a caller who does it anyway gets a silent hang rather than an error.

Activity

  1. changed the title [-]Calling sub.deinit() from a subscription's own message handler deadlocks[/-] [+]Unsubscribing from inside a message handler hangs[/+] on Aug 19, 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

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions