fix(flow-service,udp-notif-service): surface actor setup failure on early exit - #53
fix(flow-service,udp-notif-service): surface actor setup failure on early exit#53rodonile wants to merge 1 commit into
Conversation
Previously, if the actor task exited (e.g. due to a socket bind error) before replying with its local address, the handle's new() would return a generic ReceiveError, discarding the actual cause. Race the reply channel against the actor's JoinHandle so that when the actor dies first, its real error is propagated via the new ActorFailed variant instead of being masked.
There was a problem hiding this comment.
🟡 Changes recommended
The new early-failure surfacing still leaves a race where send(LocalAddr) failures return generic SendError (and the new ActorFailed variants aren’t exposed via the error chain), which can continue to hide the real startup cause.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR improves startup error reporting for flow-service and udp-notif-service actor handles by detecting when an actor task terminates before it can return its LocalAddr, and by propagating the actor’s real failure (e.g., socket bind errors) instead of a generic channel receive error.
Changes:
- Added
ActorFailed(...)variants to the handle error enums to carry the actor’s startup failure. - Updated
*_Handle::new()totokio::select!between the actorJoinHandleand theLocalAddrreply to detect early termination and surface the underlying error.
File summaries
| File | Description |
|---|---|
| crates/udp-notif-service/src/actor.rs | Adds ActorFailed(UdpNotifActorError) and races join-handle vs. local-addr response during handle creation. |
| crates/flow-service/src/flow_actor.rs | Adds ActorFailed(FlowCollectorActorError) and applies the same early-exit detection during handle creation. |
Review details
Suppressed comments (2)
crates/udp-notif-service/src/actor.rs:1045
- If the actor exits very quickly (e.g. bind() fails), cmd_tx.send(LocalAddr) can also fail with the receiver already dropped, and this path still returns SendError which hides the real cause. Since this PR is about surfacing early actor startup failures, consider awaiting the join handle on send failure and returning ActorFailed when available.
cmd_tx
.send(ActorCommand::LocalAddr(tx))
.await
.map_err(|_| ActorHandleError::SendError)?;
crates/flow-service/src/flow_actor.rs:967
- As with the udp-notif actor, if the actor task fails immediately (e.g. socket bind failure), cmd_tx.send(LocalAddr) can race and fail with the receiver already dropped. Returning SendError here still hides the actor's real startup error; awaiting the join handle on send failure would better match the PR goal of surfacing early-exit failures.
cmd_tx
.send(FlowCollectorActorCommand::LocalAddr(tx))
.await
.map_err(|_| FlowCollectorActorHandleError::SendError)?;
- Files reviewed: 2/2 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| impl std::error::Error for FlowCollectorActorHandleError { | ||
| fn description(&self) -> &str { | ||
| match *self { | ||
| FlowCollectorActorHandleError::SendError => "Error sending command to actor", | ||
| FlowCollectorActorHandleError::ReceiveError => "Error receiving response from actor", | ||
| FlowCollectorActorHandleError::ActorFailed(_) => { | ||
| "actor terminated before it could start" | ||
| } | ||
| } |
| match *self { | ||
| ActorHandleError::SendError => "error sending command to actor", | ||
| ActorHandleError::ReceiveError => "error receiving response from actor", | ||
| ActorHandleError::ActorFailed(_) => "actor terminated before it could start", | ||
| } |
Summary
FlowCollectorActorHandle::newandActorHandle::newnow race theactor's
JoinHandleagainst theLocalAddrreply channel instead ofonly awaiting the reply. This surfaces the actor's real error (e.g. a
socket bind failure) when it exits before responding, rather than a
generic
ReceiveError.Reason
If actor startup failed (e.g.
bind()error), the reply sender wasdropped without sending a response, and callers only ever saw
ReceiveError, hiding the actual cause and making failures harder todiagnose.
Changes
ActorFailed(FlowCollectorActorError)/ActorFailed(UdpNotifActorError)variants to the respective handleerror enums.
new()now usestokio::select!(biased toward the join handle) todetect early actor termination and propagate its error.