slackhq/nebula · error
INetwork.SetCategory: %s
Error message
INetwork.SetCategory: %s
What it means
This error wraps a failed HRESULT from INetwork::SetCategory, which writes a new NLM_NETWORK_CATEGORY (public/private/domain) for a Windows network. Nebula uses it to persist the configured tun.network_category onto the underlying OS network profile. A failing HRESULT means Windows refused the update — commonly due to insufficient privileges or service issues.
Source
Thrown at overlay/network_category_windows.go:243
}
func (n *iNetwork) GetCategory() (networkCategory, error) {
var c networkCategory
r1, _, _ := syscall.SyscallN(n.Vtbl.GetCategory,
uintptr(unsafe.Pointer(n)), uintptr(unsafe.Pointer(&c)),
)
if hr := hresult(r1); hr.failed() {
return 0, fmt.Errorf("INetwork.GetCategory: %s", hr)
}
return c, nil
}
func (n *iNetwork) SetCategory(c networkCategory) error {
r1, _, _ := syscall.SyscallN(n.Vtbl.SetCategory,
uintptr(unsafe.Pointer(n)), uintptr(int32(c)),
)
if hr := hresult(r1); hr.failed() {
return fmt.Errorf("INetwork.SetCategory: %s", hr)
}
return nil
}
// coInit initializes COM for the current OS thread. The returned function must
// be deferred to balance a successful init. RPC_E_CHANGED_MODE means COM is
// already initialized in a different mode on this thread, which is still fine
// for our calls but we must not Uninitialize in that case.
func coInit() (func(), error) {
err := windows.CoInitializeEx(0, windows.COINIT_MULTITHREADED)
if err == nil {
return windows.CoUninitialize, nil
}
if e, ok := err.(syscall.Errno); ok {
switch uint32(e) {
case hrSFALSE:
return windows.CoUninitialize, nil
case hrRPCEChangedMode:View on GitHub (pinned to dd8f660c0a)
Solutions
- Run the process elevated (or as LocalSystem) so it has permission to change network profiles
- Verify the Network List Service (netprofm) is running before startup
- Check the hresult in the message: E_ACCESSDENIED → privileges; service errors → restart netprofm
- Re-acquire the INetwork pointer via GetNetworkConnections/GetNetwork if the network profile changed before the write
Example fix
// before: ignoring why the write failed
if err := net.SetCategory(cat); err != nil { return err }
// after: distinguish privilege failures
if err := net.SetCategory(cat); err != nil {
if strings.Contains(err.Error(), "Access is denied") {
return fmt.Errorf("setting network category requires elevation: %w", err)
}
return err
} Defensive patterns
Strategy: try-catch
Validate before calling
// check write permission context before attempting SetCategory
func canSetCategory() error {
if !isAdminOrLocalSystem() {
return fmt.Errorf("changing network category requires elevation")
}
return nil
} Type guard
func isSetCategoryError(err error) bool {
return err != nil && strings.HasPrefix(err.Error(), "INetwork.SetCategory:")
} Try / catch
err := net.SetCategory(cat)
if err != nil {
log.Warnf("could not persist network category (running elevated?): %v", err)
// continue startup; category persistence is secondary
} Prevention
- Run the tunnel service elevated/LocalSystem when network_category is configured
- Confirm the Network List Service is running before writing
- Check the hresult for E_ACCESSDENIED and surface a clear privilege hint
- Re-acquire the INetwork pointer if the profile changed before the write
When it happens
Trigger: Calling SetCategory(c) during tun setup when applying the user's configured network_category, and the NLM COM vtbl call returns a failing HRESULT (e.g. E_ACCESSDENIED from a non-elevated process).
Common situations: Running Nebula as a non-admin service without rights to change network profiles; group policy locking network profile changes; NLM service stopped; stale INetwork pointer after profile changes.
Related errors
- INetwork.GetCategory: %s
- errAdapterNotFound
- INetworkListManager.GetNetworkConnections: %s
- IEnumNetworkConnections.Next: %s
- INetworkConnection.GetAdapterId: %s
AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03).
Data as JSON: /api/errors/759f2a180a4dec57.
Report an issue: GitHub.