{"record":{"id":"9b4cb9d8163169c9","repo":"juanfont/headscale","slug":"s-sending-confirm-request-w","errorCode":null,"errorMessage":"%s sending confirm request: %w","messagePattern":"(.+?) sending confirm request: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"integration/scenario.go","lineNumber":1321,"sourceCode":"\n\tlog.Printf(\"%s auto-submitting confirm form: %s\", hostname, confirmURL)\n\n\tformData := url.Values{\n\t\t\"headscale_register_confirm\": {csrfToken},\n\t}\n\n\tctx := context.Background()\n\n\treq, err := http.NewRequestWithContext(ctx, http.MethodPost, confirmURL.String(), strings.NewReader(formData.Encode()))\n\tif err != nil {\n\t\treturn \"\", nil, fmt.Errorf(\"%s creating confirm request: %w\", hostname, err)\n\t}\n\n\treq.Header.Set(\"Content-Type\", \"application/x-www-form-urlencoded\")\n\n\tconfirmResp, err := hc.Do(req)\n\tif err != nil {\n\t\treturn \"\", nil, fmt.Errorf(\"%s sending confirm request: %w\", hostname, err)\n\t}\n\tdefer confirmResp.Body.Close()\n\n\tconfirmBytes, err := io.ReadAll(confirmResp.Body)\n\tif err != nil {\n\t\treturn \"\", nil, fmt.Errorf(\"%s reading confirm response: %w\", hostname, err)\n\t}\n\n\tif confirmResp.StatusCode != http.StatusOK {\n\t\treturn string(confirmBytes), nil, fmt.Errorf( //nolint:err113\n\t\t\t\"%s confirm returned status %d: %s\",\n\t\t\thostname, confirmResp.StatusCode, string(confirmBytes),\n\t\t)\n\t}\n\n\treturn string(confirmBytes), nil, nil\n}\n","sourceCodeStart":1303,"sourceCodeEnd":1339,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/integration/scenario.go#L1303-L1339","documentation":"`hc.Do(req)` failed while POSTing the confirm form — a transport-level error (connection refused/reset, DNS failure, timeout), not an HTTP status. The wrapper annotates it with the client hostname so you can tell which tailscale node's registration failed.","triggerScenarios":"The headscale container is not listening on the confirm URL's host:port, the Docker network is down, TLS handshake fails (the client skips verify but the server may still break), or the context deadline expires during the POST.","commonSituations":"Headscale container crashed between the GET and the POST; the URL points at a host name resolvable only inside a Docker network the client is not attached to; test environment DNS flake.","solutions":["Check the wrapped error: 'connection refused' means the server is down — inspect the headscale container logs.","Verify the confirm URL host matches the address the scenario actually reaches headscale on.","Retry once — integration tests over Docker bridges throw transient resets.","Increase the client timeout if the wrapped error is a deadline exceeded."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Retry transport failures once; surface HTTP-status failures immediately.\nresp, err := hc.Do(req)\nif err != nil {\n    time.Sleep(500 * time.Millisecond)\n    resp, err = hc.Do(req)\n    if err != nil {\n        return \"\", nil, fmt.Errorf(\"%s sending confirm request: %w\", hostname, err)\n    }\n}","preventionTips":["Confirm headscale is reachable (health endpoint) before starting registration flows.","Set an explicit, generous Timeout on the http.Client instead of the default none.","Keep the confirm URL host identical to the one the initial GET succeeded on."],"tags":["http","network","integration","docker"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}