slackhq/nebula · error
INetwork.GetCategory: %s
Error message
INetwork.GetCategory: %s
What it means
This error wraps a failed HRESULT from INetwork::GetCategory, which reads the NLM_NETWORK_CATEGORY (public/private/domain) of a Windows network. It is the core query used to map the OS category onto Nebula's networkCategory during tun setup. A failing HRESULT means the COM call failed rather than the category being unknown.
Source
Thrown at overlay/network_category_windows.go:233
IsConnected uintptr
GetConnectivity uintptr
GetCategory uintptr
SetCategory uintptr
}
type iNetwork struct{ Vtbl *iNetworkVtbl }
func (n *iNetwork) Release() {
syscall.SyscallN(n.Vtbl.Release, uintptr(unsafe.Pointer(n)))
}
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.View on GitHub (pinned to dd8f660c0a)
Solutions
- Retry the classification once with a freshly obtained INetwork pointer
- Verify the Network List Service (netprofm) is running and healthy
- Keep the COM object usage on the thread where COM was initialized
- Parse the hresult in the message for the specific COM failure and degrade gracefully (leave category unset) when classification is best-effort
Example fix
// before
cat, err := net.GetCategory()
if err != nil { return err }
// after
cat, err := net.GetCategory()
if err != nil {
l.WithError(err).Warn("unable to read network category; leaving category unset")
return 0, false, nil
} Defensive patterns
Strategy: retry
Validate before calling
// preflight service availability
func nlmReady() bool {
state, err := svcQuery("netprofm")
return err == nil && state == "RUNNING"
} Type guard
func isGetCategoryError(err error) bool {
return err != nil && strings.HasPrefix(err.Error(), "INetwork.GetCategory:")
} Try / catch
cat, err := net.GetCategory()
if err != nil {
time.Sleep(250 * time.Millisecond)
if n2, rerr := refreshNetwork(); rerr == nil {
cat, err = n2.GetCategory()
}
if err != nil {
log.Warnf("category read failed, leaving unset: %v", err)
return 0, false, nil
}
} Prevention
- Use fresh INetwork pointers; stale ones fail transient calls
- Keep the COM apartment consistent for the entire classification chain
- Retry transient HRESULT failures once before giving up
- Default to leaving the category unset instead of failing tunnel startup
When it happens
Trigger: Calling GetCategory() on an iNetwork pointer obtained via GetNetwork() while classifying the tunnel adapter, when the COM vtbl call returns a failed HRESULT.
Common situations: Transient NLM service failure during tunnel startup; stale INetwork pointer after the network profile changed or was deleted; COM apartment/threading violations in service contexts.
Related errors
- INetwork.SetCategory: %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/3033c567f39d97a5.
Report an issue: GitHub.