netbirdio/netbird · error
receive expose event: %v
Error message
receive expose event: %v
What it means
The first stream.Recv() on the server-streamed ExposeService call returned a gRPC error instead of the Ready event. Unlike the initial-RPC failure, the call was accepted and the stream opened — this error means the daemon failed (or the transport broke) while setting the exposure up and sending its first event. The gRPC status message is surfaced.
Source
Thrown at client/cmd/expose.go:240
case expose.ProtocolHTTP:
return proto.ExposeProtocol_EXPOSE_HTTP, nil
case expose.ProtocolHTTPS:
return proto.ExposeProtocol_EXPOSE_HTTPS, nil
case expose.ProtocolTCP:
return proto.ExposeProtocol_EXPOSE_TCP, nil
case expose.ProtocolUDP:
return proto.ExposeProtocol_EXPOSE_UDP, nil
case expose.ProtocolTLS:
return proto.ExposeProtocol_EXPOSE_TLS, nil
default:
return 0, fmt.Errorf("unhandled protocol type: %d", p)
}
}
func handleExposeReady(cmd *cobra.Command, stream proto.DaemonService_ExposeServiceClient, port uint64) error {
event, err := stream.Recv()
if err != nil {
return fmt.Errorf("receive expose event: %v", status.Convert(err).Message())
}
ready, ok := event.Event.(*proto.ExposeServiceEvent_Ready)
if !ok {
return fmt.Errorf("unexpected expose event: %T", event.Event)
}
printExposeReady(cmd, ready.Ready, port)
return nil
}
func printExposeReady(cmd *cobra.Command, r *proto.ExposeServiceReady, port uint64) {
cmd.Println("Service exposed successfully!")
cmd.Printf(" Name: %s\n", r.ServiceName)
if r.ServiceUrl != "" {
cmd.Printf(" URL: %s\n", r.ServiceUrl)
}
if r.Domain != "" && !isPortBasedProtocol(exposeProtocol) {
cmd.Printf(" Domain: %s\n", r.Domain)View on GitHub (pinned to 93e97f4bf1)
Solutions
- Check the daemon log at the failure timestamp for the server-side reason (it usually knows more than the status message)
- Re-run the command — transient allocation and stream errors typically clear on retry
- If it fails consistently, verify daemon-to-management connectivity and that the account's services feature works (e.g. `netbird status` healthy, dashboard shows services)
- Avoid sending SIGINT before the Ready event is printed; wait for the success line before interrupting
Defensive patterns
Strategy: try-catch
Try / catch
event, err := stream.Recv()
if err != nil {
if ctx.Err() != nil || status.Code(err) == codes.Canceled {
return fmt.Errorf("expose interrupted before ready: %w", err)
}
// daemon-side setup failure before the Ready event; its log has the reason
return fmt.Errorf("receive expose event: %s", status.Convert(err).Message())
} Prevention
- Do not send SIGINT/SIGTERM while waiting for the Ready line — exposeFn cancels the stream and produces this error
- Re-run once on transient failures before investigating; allocation errors are often momentary
- Correlate with the daemon log timestamp when the message is generic
- Keep daemon and management versions compatible so setup-side rejections are rare
When it happens
Trigger: The daemon encounters a setup failure after accepting the RPC (proxy-cluster allocation error, management rejection) and sends an error status before Ready; the daemon process dies mid-setup; the context is canceled — exposeFn cancels ctx on SIGINT/SIGTERM, making Recv return context.Canceled; an IPC/socket failure on the open stream.
Common situations: Pressing Ctrl+C while waiting for the 'Service exposed successfully!' line; transient cluster allocation failures; daemon crash or restart during exposure setup; flaky daemon-to-management connectivity on the server side.
Related errors
- expose service: %v
- failed to get status: %v
- failed to get log level: %v
- failed to set log level to TRACE: %v
- failed to set sync response persistence: %v
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/8e4c4d1e5652d49a.
Report an issue: GitHub.