{"record":{"id":"88931137ce450721","repo":"tailscale/tailscale","slug":"failed-to-enable-s-v","errorCode":null,"errorMessage":"failed to enable %s: %v","messagePattern":"failed to enable (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"chirp/chirp.go","lineNumber":88,"sourceCode":"\t\treturn nil\n\t} else if strings.Contains(out, fmt.Sprintf(\"%s: disabled\", protocol)) {\n\t\treturn nil\n\t}\n\treturn fmt.Errorf(\"failed to disable %s: %v\", protocol, out)\n}\n\n// EnableProtocol enables the provided protocol.\nfunc (b *BIRDClient) EnableProtocol(protocol string) error {\n\tout, err := b.exec(\"enable %s\", protocol)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif strings.Contains(out, fmt.Sprintf(\"%s: already enabled\", protocol)) {\n\t\treturn nil\n\t} else if strings.Contains(out, fmt.Sprintf(\"%s: enabled\", protocol)) {\n\t\treturn nil\n\t}\n\treturn fmt.Errorf(\"failed to enable %s: %v\", protocol, out)\n}\n\n// BIRD CLI docs from https://bird.network.cz/?get_doc&v=20&f=prog-2.html#ss2.9\n\n// Each session of the CLI consists of a sequence of request and replies,\n// slightly resembling the FTP and SMTP protocols.\n// Requests are commands encoded as a single line of text,\n// replies are sequences of lines starting with a four-digit code\n// followed by either a space (if it's the last line of the reply) or\n// a minus sign (when the reply is going to continue with the next line),\n// the rest of the line contains a textual message semantics of which depends on the numeric code.\n// If a reply line has the same code as the previous one and it's a continuation line,\n// the whole prefix can be replaced by a single white space character.\n//\n// Reply codes starting with 0 stand for ‘action successfully completed’ messages,\n// 1 means ‘table entry’, 8 ‘runtime error’ and 9 ‘syntax error’.\n\nfunc (b *BIRDClient) exec(cmd string, args ...any) (string, error) {","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/tailscale/tailscale/blob/cfe32b8be6a33f8e24fbc369cbfbf7c729d9e042/chirp/chirp.go#L70-L106","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nif err := birdc.EnableProtocol(\"BGP1\"); err != nil {\n\tlog.Fatal(err) // failed to enable BGP1: 0020 No protocols match BGP1\n}\n\n// after: use the exact protocol name from the loaded config\nif err := birdc.EnableProtocol(\"bgp_peer_1\"); err != nil {\n\tlog.Fatalf(\"enable failed, bird said: %v\", err)\n}","handlingStrategy":"validation","validationCode":"// Before enabling, confirm the protocol exists in BIRD's running config.\n// chirp has no list API, so shell out once (or pre-check your bird.conf):\nout, err := exec.Command(\"birdc\", \"show\", \"protocols\").Output()\nif err != nil {\n\treturn err\n}\nif !strings.Contains(string(out), protocolName) {\n\treturn fmt.Errorf(\"protocol %q not present in BIRD; fix config first\", protocolName)\n}\nerr = birdc.EnableProtocol(protocolName)","typeGuard":null,"tryCatchPattern":"if err := birdc.EnableProtocol(p); err != nil {\n\tif strings.HasPrefix(err.Error(), \"failed to enable \") {\n\t\t// BIRD replied with a refusal; the raw reply is in the message tail\n\t\tlog.Printf(\"bird refused enable of %s: %v\", p, err)\n\t}\n\treturn err\n}","preventionTips":["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"],"tags":["go","bird","routing","bgp","unix-socket"],"backgroundTag":null,"analyzedSha":"cfe32b8be6a33f8e24fbc369cbfbf7c729d9e042","analyzedAt":"2026-08-15T19:58:31.583Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}