netbirdio/netbird · error
get net: %w
Error message
get net: %w
What it means
Returned by Client.Dial when Engine.GetNet fails: either the WireGuard interface is not initialized yet (engine mid-teardown or not fully up) or the interface has no netstack ("failed to get netstack"), which is the expected condition when the client was created with NoUserspace=true (kernel mode has no userspace stack to dial from).
Source
Thrown at client/embed/embed.go:357
c.mu.Lock()
defer c.mu.Unlock()
if c.config == nil {
return profilemanager.Config{}, ErrConfigNotInitialized
}
return *c.config, nil
}
// Dial dials a network address in the netbird network.
// Not applicable if the userspace networking mode is disabled.
func (c *Client) Dial(ctx context.Context, network, address string) (net.Conn, error) {
engine, err := c.getEngine()
if err != nil {
return nil, err
}
nsnet, err := engine.GetNet()
if err != nil {
return nil, fmt.Errorf("get net: %w", err)
}
return nsnet.DialContext(ctx, network, address)
}
// DialContext dials a network address in the netbird network with context
func (c *Client) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
return c.Dial(ctx, network, address)
}
// ListenTCP listens on the given address in the netbird network.
// Not applicable if the userspace networking mode is disabled.
func (c *Client) ListenTCP(address string) (net.Listener, error) {
nsnet, addr, err := c.getNet()
if err != nil {
return nil, err
}
View on GitHub (pinned to 93e97f4bf1)
Solutions
- If you need Dial/NewHTTPClient, create the client with NoUserspace=false (the default userspace/netstack mode).
- Guard calls with the client lifecycle: only dial between successful Start and Stop, and stop outstanding requests before Stop.
- Retry briefly if the error is a transient not-yet-initialized window.
Example fix
// before
client, err := embed.New(embed.Options{NoUserspace: true, ...})
conn, err := client.Dial(ctx, "tcp", "10.10.0.5:8080")
// after
client, err := embed.New(embed.Options{NoUserspace: false, ...})
conn, err := client.Dial(ctx, "tcp", "10.10.0.5:8080") Defensive patterns
Strategy: validation
Validate before calling
if opts.NoUserspace {
return errors.New("dial APIs require userspace mode; set NoUserspace=false")
} Try / catch
conn, err := client.Dial(ctx, network, addr)
if err != nil {
if errors.Is(err, embed.ErrClientNotStarted) || errors.Is(err, embed.ErrEngineNotStarted) || strings.Contains(err.Error(), "get net") {
// lifecycle/mode problem: not retried away by looping
}
} Prevention
- Decide up front whether you need in-process dialing; if yes, never set NoUserspace=true.
- Shut down http.Clients built from NewHTTPClient before calling Stop to avoid dial races.
When it happens
Trigger: Calling Dial after Start succeeded but with NoUserspace=true in Options; calling Dial concurrently with Stop so the interface is already torn down; calling Dial in the window before the interface is fully initialized.
Common situations: An embedding app that toggles NoUserspace for performance finds Dial/DialContext/NewHTTPClient all failing; races between an HTTP client built via NewHTTPClient and client shutdown; dialing right after Start returns on a slow engine.
Related errors
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/774d6be2e5c024f1.
Report an issue: GitHub.