netbirdio/netbird · error

device is not ready yet

Error message

device is not ready yet

What it means

Returned by TunDevice.Up() (userspace unix) when t.device is nil. t.device is only set inside Create() after the tun device is opened, so this error indicates Up() was called before a successful Create(), or a Create() error was ignored. Same lifecycle contract as the netstack variant.

Source

Thrown at client/iface/device/device_usp_unix.go:80

	err = t.assignAddr()
	if err != nil {
		t.device.Close()
		return nil, fmt.Errorf("error assigning ip: %s", err)
	}

	t.configurer = configurer.NewUSPConfigurer(t.device, t.name, t.iceBind.ActivityRecorder())
	err = t.configurer.ConfigureInterface(t.key, t.port)
	if err != nil {
		t.device.Close()
		t.configurer.Close()
		return nil, fmt.Errorf("error configuring interface: %s", err)
	}
	return t.configurer, nil
}

func (t *TunDevice) Up() (*udpmux.UniversalUDPMuxDefault, error) {
	if t.device == nil {
		return nil, fmt.Errorf("device is not ready yet")
	}

	err := t.device.Up()
	if err != nil {
		return nil, err
	}

	udpMux, err := t.iceBind.GetICEMux()
	if err != nil {
		return nil, err
	}
	t.udpMux = udpMux

	log.Debugf("device is ready to use: %s", t.name)
	return udpMux, nil
}

func (t *TunDevice) UpdateAddr(address wgaddr.Address) error {

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Guarantee Create() succeeded (error checked) before calling Up()
  2. On reconnect paths, treat this error as a signal to re-run Create() first
  3. Protect device lifecycle transitions with a mutex or single state owner

Example fix

// before
mux, err := dev.Up()

// after
if _, cerr := dev.Create(); cerr != nil {
    return nil, cerr
}
mux, err := dev.Up()
Defensive patterns

Strategy: validation

Validate before calling

// Up() requires a device created by a successful Create()
if dev.Device() == nil {
    if _, err := dev.Create(); err != nil {
        return nil, err
    }
}

Type guard

func deviceReady(dev *device.TunDevice) bool {
    return dev != nil && dev.Device() != nil
}

Try / catch

mux, err := dev.Up()
if err != nil && strings.Contains(err.Error(), "device is not ready yet") {
    // ordering bug: create first, then retry Up()
}

Prevention

When it happens

Trigger: Calling Up() before Create(); ignoring the error from Create() and continuing; Close() and Up() racing from different goroutines.

Common situations: Reconnect logic that retries Up() without re-running Create(); concurrent engine stop/start; tests calling Up() directly.

Related errors


AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16). Data as JSON: /api/errors/cb69100e96956874. Report an issue: GitHub.