tailscale/tailscale · error

failed to enable %s: %v

Error message

failed to enable %s: %v

What it means

Returned by BIRDClient.EnableProtocol (chirp/chirp.go:88) when the `enable <protocol>` command was sent to the BIRD routing daemon over its Unix control socket, but the reply text contains neither `<protocol>: already enabled` nor `<protocol>: enabled`. The raw BIRD reply is embedded via %v, so the message tells you exactly how BIRD refused the command. Per the BIRD CLI protocol notes above the code, reply codes starting with 8 (runtime error) or 9 (syntax error) typically end up here.

Source

Thrown at chirp/chirp.go:88

		return nil
	} else if strings.Contains(out, fmt.Sprintf("%s: disabled", protocol)) {
		return nil
	}
	return fmt.Errorf("failed to disable %s: %v", protocol, out)
}

// EnableProtocol enables the provided protocol.
func (b *BIRDClient) EnableProtocol(protocol string) error {
	out, err := b.exec("enable %s", protocol)
	if err != nil {
		return err
	}
	if strings.Contains(out, fmt.Sprintf("%s: already enabled", protocol)) {
		return nil
	} else if strings.Contains(out, fmt.Sprintf("%s: enabled", protocol)) {
		return nil
	}
	return fmt.Errorf("failed to enable %s: %v", protocol, out)
}

// BIRD CLI docs from https://bird.network.cz/?get_doc&v=20&f=prog-2.html#ss2.9

// Each session of the CLI consists of a sequence of request and replies,
// slightly resembling the FTP and SMTP protocols.
// Requests are commands encoded as a single line of text,
// replies are sequences of lines starting with a four-digit code
// followed by either a space (if it's the last line of the reply) or
// a minus sign (when the reply is going to continue with the next line),
// the rest of the line contains a textual message semantics of which depends on the numeric code.
// If a reply line has the same code as the previous one and it's a continuation line,
// the whole prefix can be replaced by a single white space character.
//
// Reply codes starting with 0 stand for ‘action successfully completed’ messages,
// 1 means ‘table entry’, 8 ‘runtime error’ and 9 ‘syntax error’.

func (b *BIRDClient) exec(cmd string, args ...any) (string, error) {

View on GitHub (pinned to cfe32b8be6)

Solutions

  1. Read the BIRD reply embedded in the error message — it states the actual reason (unknown protocol, runtime error, etc.)
  2. Verify the exact protocol name with `birdc show protocols` and compare it to the string passed to EnableProtocol
  3. Check the BIRD config the daemon actually loaded (`birdc show config`) to confirm the protocol exists and can be enabled
  4. Check BIRD's own log for runtime errors (e.g. interface missing, BGP peer unreachable) that make the enable fail
  5. If the reply text looks correct but still unmatched, check for a BIRD version difference in reply wording and adjust the match strings

Example fix

// before
if err := birdc.EnableProtocol("BGP1"); err != nil {
	log.Fatal(err) // failed to enable BGP1: 0020 No protocols match BGP1
}

// after: use the exact protocol name from the loaded config
if err := birdc.EnableProtocol("bgp_peer_1"); err != nil {
	log.Fatalf("enable failed, bird said: %v", err)
}
Defensive patterns

Strategy: validation

Validate before calling

// Before enabling, confirm the protocol exists in BIRD's running config.
// chirp has no list API, so shell out once (or pre-check your bird.conf):
out, err := exec.Command("birdc", "show", "protocols").Output()
if err != nil {
	return err
}
if !strings.Contains(string(out), protocolName) {
	return fmt.Errorf("protocol %q not present in BIRD; fix config first", protocolName)
}
err = birdc.EnableProtocol(protocolName)

Try / catch

if err := birdc.EnableProtocol(p); err != nil {
	if strings.HasPrefix(err.Error(), "failed to enable ") {
		// BIRD replied with a refusal; the raw reply is in the message tail
		log.Printf("bird refused enable of %s: %v", p, err)
	}
	return err
}

Prevention

When it happens

Trigger: Calling chirp's EnableProtocol with a protocol name that does not exist in the running BIRD config (BIRD answers `0020 No protocols match ...`); enabling a protocol that BIRD refuses (e.g. it is down at the config level or dependent on an unavailable table/interface); a BIRD version whose success message text differs so neither strings.Contains check matches.

Common situations: Test harnesses and scripts (chirp is used to toggle BGP protocols in Tailscale's BIRD-based tests) where the protocol was renamed or removed from bird.conf; typos in the protocol name; leftover chirp sessions after the bird config was regenerated with different protocol names.

Related errors


AI-assisted analysis of tailscale/tailscale@cfe32b8be6 (2026-08-15). Data as JSON: /api/errors/88931137ce450721. Report an issue: GitHub.