slackhq/nebula · error
must be using user device
Error message
must be using user device
What it means
service.New wraps a nebula Control into the service (device-tunnel) API. It requires the control's device be an *overlay.UserDevice (tun backed by a user-space device handle); any other device type fails with this error before Start is called. The service layer needs the user device to expose file-descriptor based tunneling to the OS.
Source
Thrown at service/service.go:50
const nicID = 1
type Service struct {
eg *errgroup.Group
control *nebula.Control
ipstack *stack.Stack
mu struct {
sync.Mutex
listeners map[uint16]*tcpListener
}
}
func New(control *nebula.Control) (_ *Service, reterr error) {
// Check this before Start so a failure doesn't leave a running nebula
device, ok := control.Device().(*overlay.UserDevice)
if !ok {
return nil, errors.New("must be using user device")
}
err := control.Start()
if err != nil {
return nil, err
}
// Anything that fails after a successful Start must tear nebula back down
defer func() {
if reterr != nil {
control.Stop()
}
}()
ctx := control.Context()
eg, ctx := errgroup.WithContext(ctx)
s := Service{
eg: eg,View on GitHub (pinned to dd8f660c0a)
Solutions
- Start nebula with the user device enabled so control.Device() returns *overlay.UserDevice (service mode requires it).
- Check the config/CLI flags that select device type and switch from kernel tun to user device.
- If you don't need service/device-tunnel functionality, use the plain nebula Control API instead of service.New.
Example fix
// before (kernel tun) control, _ := nebula.Main(config, false, "", nil, nil) s, err := service.New(control) // panics into error: must be using user device // after: launch with user device support, e.g. // nebula -service -config config.yaml (which configures overlay.UserDevice)
Defensive patterns
Strategy: type-guard
Validate before calling
if _, ok := control.Device().(*overlay.UserDevice); !ok {
return errors.New("service.New requires a user device; enable user-space tun")
} Type guard
func isUserDevice(control *nebula.Control) bool {
_, ok := control.Device().(*overlay.UserDevice)
return ok
} Try / catch
svc, err := service.New(control)
if err != nil {
if err.Error() == "must be using user device" {
return fmt.Errorf("service mode requires the user device; check your device/tun configuration")
}
return err
} Prevention
- Assert the device type before calling service.New to fail with a clearer message
- Enable user-device (service) mode in config/CLI when using the service API
- Document that plain kernel-tun setups are incompatible with service.New
When it happens
Trigger: Calling service.New(control) (from doService, run, or newSimpleService) when control.Device() type-asserts to anything other than *overlay.UserDevice — e.g. the node was started with a regular kernel tun device.
Common situations: Running nebula in an environment without the user-device tun option enabled; config using a normal tun interface where service mode expects `-usb`/user device support; embedding the service API against a control built with default tun settings.
Related errors
- no inside interface (tun)
- only tcp is supported
- specific port required, got 0
- Send ring corrupt
- ErrPeerRejected
AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03).
Data as JSON: /api/errors/edacbd946c4375ec.
Report an issue: GitHub.