{"record":{"id":"69ea3076548feeed","repo":"netbirdio/netbird","slug":"create-nic-v","errorCode":null,"errorMessage":"create NIC: %v","messagePattern":"create NIC: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/firewall/uspfilter/forwarder/forwarder.go","lineNumber":84,"sourceCode":"\t\t},\n\t\tTransportProtocols: []stack.TransportProtocolFactory{\n\t\t\ttcp.NewProtocol,\n\t\t\tudp.NewProtocol,\n\t\t\ticmp.NewProtocol4,\n\t\t\ticmp.NewProtocol6,\n\t\t},\n\t\tHandleLocal: false,\n\t})\n\n\tnicID := tcpip.NICID(1)\n\tendpoint := &endpoint{\n\t\tlogger: logger,\n\t\tdevice: iface.GetWGDevice(),\n\t}\n\tendpoint.mtu.Store(uint32(mtu))\n\n\tif err := s.CreateNIC(nicID, endpoint); err != nil {\n\t\treturn nil, fmt.Errorf(\"create NIC: %v\", err)\n\t}\n\n\tprotoAddr := tcpip.ProtocolAddress{\n\t\tProtocol: ipv4.ProtocolNumber,\n\t\tAddressWithPrefix: tcpip.AddressWithPrefix{\n\t\t\tAddress:   tcpip.AddrFrom4(iface.Address().IP.As4()),\n\t\t\tPrefixLen: iface.Address().Network.Bits(),\n\t\t},\n\t}\n\n\tif err := s.AddProtocolAddress(nicID, protoAddr, stack.AddressProperties{}); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to add protocol address: %s\", err)\n\t}\n\n\tif v6 := iface.Address().IPv6; v6.IsValid() {\n\t\tv6Addr := tcpip.ProtocolAddress{\n\t\t\tProtocol: ipv6.ProtocolNumber,\n\t\t\tAddressWithPrefix: tcpip.AddressWithPrefix{","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/firewall/uspfilter/forwarder/forwarder.go#L66-L102","documentation":"Returned by forwarder.New (client/firewall/uspfilter/forwarder/forwarder.go:84) when the gVisor netstack rejects s.CreateNIC(1, endpoint): tcpip errors such as errInvalidNICID (duplicate NIC 1 when New is called twice on one stack), a nil/misbehaving endpoint (LinkEndpoint hooks), or stack resource limits. The endpoint wraps the WireGuard device for injecting packets, so a device that cannot be attached also lands here. Note the wrapper uses %v, so the tcpip error string is embedded without an error chain.","triggerScenarios":"initForwarder racing so forwarder.New runs twice against a shared stack (NIC 1 already exists); endpoint.device nil because GetWGDevice() returned a non-nil but unusable device; gVisor stack created with options incompatible with a custom endpoint.","commonSituations":"Concurrent EnableRouting/determineRouting paths constructing the forwarder twice; version drift in gvisor/netstack changing CreateNIC semantics; embedded netstack mode with a partially wired device.","solutions":["Guarantee single construction: initForwarder already returns early when m.forwarder is non-nil - ensure all callers go through it under the manager mutex","Validate the device endpoint before New (non-nil device, mtu set) and fail with a precise message","Use a fresh stack per forwarder instance so NICID 1 can never collide","If hit after an upgrade, check the gvisor version's CreateNIC contract for new error conditions"],"exampleFix":"// before\nif err := s.CreateNIC(nicID, endpoint); err != nil {\n    return nil, fmt.Errorf(\"create NIC: %v\", err)\n}\n// after - wrap the error and include the NIC id for diagnosis\nif err := s.CreateNIC(nicID, endpoint); err != nil {\n    return nil, fmt.Errorf(\"create NIC %d: %w\", nicID, err)\n}","handlingStrategy":"validation","validationCode":"if wgIface.GetWGDevice() == nil {\n    return fmt.Errorf(\"cannot init forwarder: no userspace device to attach\")\n}\nif mtu == 0 {\n    mtu = iface.DefaultMTU\n}\n_ = fw.EnableRouting()","typeGuard":"func canCreateForwarder(i common.IFaceMapper, mtu int) bool {\n    return i.GetWGDevice() != nil && i.Address().IP.IsValid() && mtu > 0\n}","tryCatchPattern":"if err := fw.EnableRouting(); err != nil {\n    if strings.Contains(err.Error(), \"create NIC\") {\n        // stack is in an unknown state; rebuild the forwarder once\n        fw.Reset()\n        if retryErr := fw.EnableRouting(); retryErr == nil {\n            return nil\n        }\n    }\n    return err\n}","preventionTips":["Ensure only one forwarder is ever constructed per manager (guard with atomic pointer check)","Give each forwarder its own gVisor stack to make NICID collisions impossible","Wrap tcpip errors with %w so NIC creation failures are matchable by callers"],"tags":["gvisor","netstack","forwarder","uspfilter"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}