{"record":{"id":"6bc4296a806a5810","repo":"juanfont/headscale","slug":"checking-health-w","errorCode":null,"errorMessage":"checking health: %w","messagePattern":"checking health: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/headscale/cli/health.go","lineNumber":23,"sourceCode":"\t\"fmt\"\n\t\"net/http\"\n\n\tclientv1 \"github.com/juanfont/headscale/gen/client/v1\"\n\t\"github.com/spf13/cobra\"\n)\n\nfunc init() {\n\trootCmd.AddCommand(healthCmd)\n}\n\nvar healthCmd = &cobra.Command{\n\tUse:   \"health\",\n\tShort: \"Check the health of the Headscale server\",\n\tLong:  \"Check the health of the Headscale server. This command will return an exit code of 0 if the server is healthy, or 1 if it is not.\",\n\tRunE: clientRunE(func(ctx context.Context, client *clientv1.ClientWithResponses, cmd *cobra.Command, args []string) error {\n\t\tresp, err := client.HealthWithResponse(ctx)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"checking health: %w\", err)\n\t\t}\n\n\t\tif resp.StatusCode() != http.StatusOK {\n\t\t\treturn apiError(resp.StatusCode(), resp.ApplicationproblemJSONDefault)\n\t\t}\n\n\t\treturn printOutput(cmd, resp.JSON200, \"\")\n\t}),\n}\n","sourceCodeStart":5,"sourceCodeEnd":33,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/cmd/headscale/cli/health.go#L5-L33","documentation":"Transport error from 'headscale health': the GET to the health endpoint failed before a response was parsed (client.HealthWithResponse returned err). Ironic but informative — the command whose purpose is to report server health fails when the server is entirely unreachable, and this error is the signal for that case (exit code becomes non-zero).","triggerScenarios":"Running 'headscale health' when the server is not started, is bound to a different address/port than the CLI targets, TLS is misconfigured, or DNS for the configured server URL fails.","commonSituations":"First-line debugging after a failed headscale start; systemd service crashed; CLI config still pointing at default 127.0.0.1:50443 while the server listens elsewhere; monitoring probes using the CLI against a moved endpoint.","solutions":["Confirm the headscale process is actually running (systemctl status / ps)","Compare the CLI's configured address with the server's listen address","Check TLS: if the server uses self-signed certs, configure the CA in the CLI socket settings","Interpret the wrapped error — dial tcp refused, x509, and no such host each have distinct fixes"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// probe before relying on the CLI health command in scripts\nif err := probeTCP(addr, 2*time.Second); err != nil {\n\tfmt.Fprintf(os.Stderr, \"server at %s unreachable: %v\\n\", addr, err)\n\tos.Exit(1)\n}","typeGuard":null,"tryCatchPattern":"if err := healthCmd.Execute(); err != nil {\n\t// non-zero exit is the intended signal; distinguish transport (server down) from unhealthy response\n\tif strings.Contains(err.Error(), \"checking health\") { /* server unreachable */ }\n}","preventionTips":["Use 'headscale health' in monitoring with exit-code semantics","Keep CLI address/CA config synced with server deployment changes"],"tags":["network","healthcheck","cli","go"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}