tailscale/tailscale · error

get input chain: %w

Error message

get input chain: %w

What it means

AddLoopbackRule needs the standard "input" chain of the address family's filter table and this error means getChainFromTable couldn't get it. Two distinct causes share this wrap: errorChainNotFound ('chain input not found in table filter') when the base chains were never created — the doc for newNfTablesRunner explicitly says it does NOT guarantee tables/chains exist — or the underlying 'list chains' netlink failure. Callers that skipped base setup hit the first; broken environments the second.

Source

Thrown at util/linuxfw/nftables_runner.go:876

		return nil, fmt.Errorf("nftables for IPv6 are not available on this host")
	}
	if addr.Is6() {
		return n.nft6, nil
	}
	return n.nft4, nil
}

// AddLoopbackRule adds an nftables rule to permit loopback traffic to
// a local Tailscale IP. This rule is added only if it does not already exist.
func (n *nftablesRunner) AddLoopbackRule(addr netip.Addr) error {
	nf, err := n.getNFTByAddr(addr)
	if err != nil {
		return fmt.Errorf("error setting up nftables for IP family of %v: %w", addr, err)
	}

	inputChain, err := getChainFromTable(n.conn, nf.Filter, chainNameInput)
	if err != nil {
		return fmt.Errorf("get input chain: %w", err)
	}

	if err := insertLoopbackRule(n.conn, nf.Proto, nf.Filter, inputChain, addr); err != nil {
		return fmt.Errorf("add loopback rule: %w", err)
	}

	return nil
}

// DelLoopbackRule removes the nftables rule permitting loopback
// traffic to a Tailscale IP.
func (n *nftablesRunner) DelLoopbackRule(addr netip.Addr) error {
	nf, err := n.getNFTByAddr(addr)
	if err != nil {
		return fmt.Errorf("error setting up nftables for IP family of %v: %w", addr, err)
	}

	inputChain, err := getChainFromTable(n.conn, nf.Filter, chainNameInput)

View on GitHub (pinned to 6e0912f979)

Solutions

  1. Call the runner's base setup (EnsureBase) before AddLoopbackRule — it creates the filter table chains this API depends on.
  2. Verify the chain exists: `nft list table ip filter` should show chain input.
  3. If the error chain is the netlink one (wrapped errno), fix capabilities/kernel support as for the 'list chains' error.
  4. Re-run the operation after restoring the table; AddLoopbackRule is idempotent.

Example fix

// before
err := fw.AddLoopbackRule(addr) // get input chain: chain input not found in table filter

// after
if err := fw.EnsureBase(); err != nil { // creates filter table + input chain etc.
	return err
}
err := fw.AddLoopbackRule(addr)
Defensive patterns

Strategy: validation

Validate before calling

if err := fw.EnsureBase(); err != nil { // creates filter table + input chain
	return err
}
// now AddLoopbackRule cannot hit 'chain input not found'

Type guard

func isChainNotFound(err error) bool {
	return err != nil && strings.Contains(err.Error(), "not found in table")
}

Try / catch

err := fw.AddLoopbackRule(addr)
if isChainNotFound(err) {
	// base chains absent: run EnsureBase once and retry
	if berr := fw.EnsureBase(); berr != nil {
		return berr
	}
	err = fw.AddLoopbackRule(addr)
}

Prevention

When it happens

Trigger: Calling AddLoopbackRule on a runner whose EnsureBase/chain provisioning never ran (chains missing from the filter table), on a host whose filter table or input chain was flushed/deleted by another program, or when the netlink chain listing fails (permissions/kernel support).

Common situations: Code using linuxfw.New + AddLoopbackRule without EnsureBase; external tools (nft flush ruleset, docker, firewalld reload) removing the input chain mid-run; unprivileged processes failing at the netlink dump.

Related errors


AI-assisted analysis of tailscale/tailscale@6e0912f979 (2026-08-18). Data as JSON: /api/errors/10c803553e05f8fe. Report an issue: GitHub.