{"record":{"id":"463fcbd3367053e6","repo":"tailscale/tailscale","slug":"add-table-w","errorCode":null,"errorMessage":"add table: %w","messagePattern":"add table: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"util/linuxfw/nftables_runner.go","lineNumber":421,"sourceCode":"\t\t}\n\t}\n\treturn nil, nil\n}\n\n// createTableIfNotExist creates a nftables table via connection c if it does\n// not exist within the given family.\nfunc createTableIfNotExist(c *nftables.Conn, family nftables.TableFamily, name string) (*nftables.Table, error) {\n\tif t, err := getTableIfExists(c, family, name); err != nil {\n\t\treturn nil, fmt.Errorf(\"get table: %w\", err)\n\t} else if t != nil {\n\t\treturn t, nil\n\t}\n\tt := c.AddTable(&nftables.Table{\n\t\tFamily: family,\n\t\tName:   name,\n\t})\n\tif err := c.Flush(); err != nil {\n\t\treturn nil, fmt.Errorf(\"add table: %w\", err)\n\t}\n\treturn t, nil\n}\n\ntype errorChainNotFound struct {\n\tchainName string\n\ttableName string\n}\n\nfunc (e errorChainNotFound) Error() string {\n\treturn fmt.Sprintf(\"chain %s not found in table %s\", e.chainName, e.tableName)\n}\n\n// getChainFromTable returns the chain with the given name from the given table.\n// Note that a chain name is unique within a table.\nfunc getChainFromTable(c *nftables.Conn, table *nftables.Table, name string) (*nftables.Chain, error) {\n\tif table == nil {\n\t\treturn nil, fmt.Errorf(\"could not get chain %q: table not initialized\", name)","sourceCodeStart":403,"sourceCodeEnd":439,"githubUrl":"https://github.com/tailscale/tailscale/blob/6e0912f97994f927632b34ae9e63b53d6516a6ac/util/linuxfw/nftables_runner.go#L403-L439","documentation":"createTableIfNotExist actually tried to create the table (c.AddTable + Flush) and the kernel rejected it. Classic errnos: EPERM (no CAP_NET_ADMIN), EOPNOTSUPP/ENOTSUP (kernel lacks nftables), EEXIST (another writer created the table between the probe and the flush). This is the definitive 'cannot create nftables table' signal.","triggerScenarios":"First firewall setup on a host: AddBase, AddDNATRule, EnsureSNATForDst, ClampMSSToPMTU, or svc-chain ensure creating nat/filter tables when the kernel refuses the batch.","commonSituations":"Missing capabilities in containers (most common); old or stripped kernels without CONFIG_NF_TABLES; racing creators; netlink batch overflow with huge rule sets.","solutions":["Grant CAP_NET_ADMIN (root) to the process.","Enable nftables in the kernel; verify with 'nft list tables' or 'ls /proc/net/netfilter/nfnetlink_queue' style probes.","On EEXIST races, re-run the ensure — it is idempotent and the probe will now find the table.","Fall back to the iptables runner if the kernel permanently lacks nftables."],"exampleFix":"// before\nconn := nftables.New()\nt, err := createTableIfNotExist(conn, nftables.TableFamilyIPv4, \"nat\")\n\n// after\nif err != nil {\n\tswitch {\n\tcase errors.Is(err, unix.EPERM):\n\t\treturn fmt.Errorf(\"need CAP_NET_ADMIN: %w\", err)\n\tcase errors.Is(err, unix.EOPNOTSUPP):\n\t\treturn fmt.Errorf(\"kernel lacks nftables support: %w\", err)\n\tcase errors.Is(err, unix.EEXIST):\n\t\tt, err = createTableIfNotExist(conn, nftables.TableFamilyIPv4, \"nat\") // re-probe\n\t}\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":"func isCreateRace(err error) bool { return errors.Is(err, unix.EEXIST) }\nfunc isUnsupported(err error) bool { return errors.Is(err, unix.EOPNOTSUPP) }","tryCatchPattern":"t, err := createTableIfNotExist(conn, family, name)\nif errors.Is(err, unix.EEXIST) {\n\tt, err = createTableIfNotExist(conn, family, name) // probe now finds it\n}\nif errors.Is(err, unix.EOPNOTSUPP) {\n\t// switch to iptables runner permanently\n}","preventionTips":["Grant CAP_NET_ADMIN before any table-creating API.","Treat EEXIST as a benign race: re-probe instead of failing.","Detect EOPNOTSUPP once at startup and select a supported firewall backend."],"tags":["nftables","table-creation","netlink","permissions","kernel-support"],"backgroundTag":"nftables-table-create-failed","analyzedSha":"6e0912f97994f927632b34ae9e63b53d6516a6ac","analyzedAt":"2026-08-18T08:17:25.280Z","contentChangedAt":"2026-08-18T08:17:25.280Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}