- my $tied_pp = delete($self->{rpipe}) or return finalize($self, $err);
- my PublicInbox::ProcessPipe $pp = tied *$tied_pp;
- @$pp{qw(cb arg)} = (\&waitpid_err, $self); # for ->DESTROY
+ $self->{_err} //= $err; # only for $@
+
+ # we can safely finalize if pipe was closed before, or if
+ # {_err} is defined by waitpid_err. Deleting {rpipe} will
+ # trigger PublicInbox::ProcessPipe::DESTROY -> waitpid_err,
+ # but it may not fire right away if inside the event loop.
+ my $closed_before = !delete($self->{rpipe});
+ finalize($self) if $closed_before || defined($self->{_err});