{"record":{"id":"591fbf088ef0f7c4","repo":"juanfont/headscale","slug":"creating-request-w","errorCode":null,"errorMessage":"creating request: %w","messagePattern":"creating request: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"cmd/dev/main.go","lineNumber":252,"sourceCode":"\t\treturn fmt.Errorf(\"headscale exited: %w\", err)\n\t}\n\n\treturn nil\n}\n\n// waitForHealth polls the health endpoint until it returns 200 or the\n// timeout expires.\nfunc waitForHealth(ctx context.Context, url string, timeout time.Duration) error {\n\tdeadline := time.Now().Add(timeout)\n\n\tfor time.Now().Before(deadline) {\n\t\tif ctx.Err() != nil {\n\t\t\treturn ctx.Err()\n\t\t}\n\n\t\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"creating request: %w\", err)\n\t\t}\n\n\t\tresp, err := http.DefaultClient.Do(req)\n\t\tif err == nil {\n\t\t\tresp.Body.Close()\n\n\t\t\tif resp.StatusCode == http.StatusOK {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\n\t\t// Busy-wait is acceptable for a dev tool polling a local server.\n\t\ttime.Sleep(200 * time.Millisecond) //nolint:forbidigo\n\t}\n\n\treturn errHealthTimeout\n}\n","sourceCodeStart":234,"sourceCodeEnd":270,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/cmd/dev/main.go#L234-L270","documentation":"\"creating request: %w\" at cmd/dev/main.go:252 wraps http.NewRequestWithContext inside waitForHealth's poll loop. NewRequestWithContext only fails on an invalid method or an unparseable URL, so in cmd/dev the practical cause is the health URL being malformed, or — most commonly — ctx already cancelled so the error surfaced at request construction. The URL is built as http://127.0.0.1:%d/health from the -port flag.","triggerScenarios":"Passing a -port value that renders the URL invalid (negative, or overflowing into a bad string after formatting); Ctrl+C (SIGINT/SIGTERM) cancelling the signal.NotifyContext between the loop's ctx.Err() check and request construction, surfacing context.Canceled through this wrap.","commonSituations":"Interrupting cmd/dev during startup; exotic port values from scripts computing -port dynamically.","solutions":["If you pressed Ctrl+C, this is benign shutdown noise — no action needed","Otherwise pass a sane port: `go run ./cmd/dev -port 8080` and confirm http://127.0.0.1:8080/health is the intended URL"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// sanity-check the health URL once before the poll loop\nif _, err := url.ParseRequestURI(fmt.Sprintf(\"http://127.0.0.1:%d/health\", port)); err != nil {\n\treturn fmt.Errorf(\"invalid health URL for port %d\", port)\n}","typeGuard":null,"tryCatchPattern":"req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)\nif err != nil {\n\tif ctx.Err() != nil {\n\t\treturn ctx.Err() // benign shutdown, stop polling\n\t}\n\treturn fmt.Errorf(\"creating request: %w\", err)\n}","preventionTips":["Treat this error during Ctrl+C as shutdown noise, not a bug","Pass a plain positive port number to -port"],"tags":["dev-tooling","http","health-check","context-cancellation"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}