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
- Read the BIRD reply embedded in the error message — it states the actual reason (unknown protocol, runtime error, etc.)
- Verify the exact protocol name with `birdc show protocols` and compare it to the string passed to EnableProtocol
- Check the BIRD config the daemon actually loaded (`birdc show config`) to confirm the protocol exists and can be enabled
- Check BIRD's own log for runtime errors (e.g. interface missing, BGP peer unreachable) that make the enable fail
- 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
- Derive protocol names from the same source that generates bird.conf instead of hardcoding them
- Assert on the birdc show protocols output in test setup so name drift fails early
- Treat the embedded BIRD reply as the source of truth; never retry blindly on a refusal
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
- failed to connect to BIRD: %w
- reading response from bird failed (EOF): %q
- unix socket path cannot be empty
- failed to disable %s: %v
- Failed to connect to local Tailscale daemon for %s; %s Error
AI-assisted analysis of tailscale/tailscale@cfe32b8be6 (2026-08-15).
Data as JSON: /api/errors/88931137ce450721.
Report an issue: GitHub.