{"record":{"id":"15cf7bd2f759b853","repo":"netbirdio/netbird","slug":"create-nat-output-chain-w","errorCode":null,"errorMessage":"create NAT output chain: %w","messagePattern":"create NAT output chain: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/firewall/nftables/router_linux.go","lineNumber":2000,"sourceCode":"}\n\n// ensureNATOutputChain lazily creates the OUTPUT NAT chain on first use.\nfunc (r *router) ensureNATOutputChain() error {\n\tif _, exists := r.chains[chainNameNATOutput]; exists {\n\t\treturn nil\n\t}\n\n\tr.chains[chainNameNATOutput] = r.conn.AddChain(&nftables.Chain{\n\t\tName:     chainNameNATOutput,\n\t\tTable:    r.workTable,\n\t\tHooknum:  nftables.ChainHookOutput,\n\t\tPriority: nftables.ChainPriorityNATDest,\n\t\tType:     nftables.ChainTypeNAT,\n\t})\n\n\tif err := r.conn.Flush(); err != nil {\n\t\tdelete(r.chains, chainNameNATOutput)\n\t\treturn fmt.Errorf(\"create NAT output chain: %w\", err)\n\t}\n\treturn nil\n}\n\n// AddOutputDNAT adds an OUTPUT chain DNAT rule for locally-generated traffic.\nfunc (r *router) AddOutputDNAT(localAddr netip.Addr, protocol firewall.Protocol, originalPort, translatedPort uint16) error {\n\truleID := fmt.Sprintf(\"output-dnat-%s-%s-%d-%d\", localAddr.String(), protocol, originalPort, translatedPort)\n\n\tif _, exists := r.rules[ruleID]; exists {\n\t\treturn nil\n\t}\n\n\tif err := r.ensureNATOutputChain(); err != nil {\n\t\treturn err\n\t}\n\n\tprotoNum, err := r.af.protoNum(protocol)\n\tif err != nil {","sourceCodeStart":1982,"sourceCodeEnd":2018,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/firewall/nftables/router_linux.go#L1982-L2018","documentation":"ensureNATOutputChain lazily creates the netbird-nat-output chain (NAT type, OUTPUT hook, NAT-destination priority) on first AddOutputDNAT and commits it with Flush. 'create NAT output chain' wraps that commit failure; the chain is removed from r.chains on failure so a later call retries creation from a clean slate.","triggerScenarios":"EEXIST when the chain already exists in the kernel but not in the in-memory map (state skew after external changes or restart); the workTable was flushed so NEWCHAIN targets a missing table; EPERM without CAP_NET_ADMIN.","commonSituations":"Leftover chains from a previous agent run; firewalld or hand-written nftables files defining conflicting chains; containerized agents lacking privileges.","solutions":["Check 'sudo nft list chains' for netbird-nat-output; if present but unknown to the agent, remove it or restart the agent to adopt it.","Verify the parent table still exists ('sudo nft list tables').","Ensure root or CAP_NET_ADMIN.","Retry the operation that triggered chain creation; the code path is self-healing after the map rollback."],"exampleFix":"// before\nif err := r.conn.Flush(); err != nil {\n    delete(r.chains, chainNameNATOutput)\n    return fmt.Errorf(\"create NAT output chain: %w\", err)\n}\n\n// after: adopt a pre-existing chain instead of failing\nif err := r.conn.Flush(); err != nil {\n    if errors.Is(err, unix.EEXIST) {\n        log.Warnf(\"NAT output chain already exists, adopting it\")\n        return nil\n    }\n    delete(r.chains, chainNameNATOutput)\n    return fmt.Errorf(\"create NAT output chain: %w\", err)\n}","handlingStrategy":"retry","validationCode":"// Confirm the chain is absent before triggering lazy creation\nfunc natOutputChainExists(conn *nftables.Conn, table *nftables.Table) bool {\n    _, err := conn.GetChain(table, chainNameNATOutput)\n    return err == nil\n}","typeGuard":null,"tryCatchPattern":"if err := r.conn.Flush(); err != nil {\n    if errors.Is(err, unix.EEXIST) {\n        // chain already in kernel: adopt instead of failing\n        return nil\n    }\n    delete(r.chains, chainNameNATOutput)\n    return fmt.Errorf(\"create NAT output chain: %w\", err)\n}","preventionTips":["Clean up netbird chains on agent shutdown so later runs do not meet EEXIST.","Keep external nftables files from defining chains in the netbird table.","Retry the triggering operation after a failed chain creation; the rollback makes it safe."],"tags":["nftables","netlink","nat-chain","initialization"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}