{"record":{"id":"3d645f7c6eacaf7a","repo":"juanfont/headscale","slug":"creating-metrics-request-w","errorCode":null,"errorMessage":"creating metrics request: %w","messagePattern":"creating metrics request: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"integration/hsic/hsic.go","lineNumber":742,"sourceCode":"\n\terr := dockertestutil.WriteLog(t.pool, t.container, &stdout, &stderr)\n\tif err != nil {\n\t\treturn \"\", \"\", fmt.Errorf(\"reading container logs: %w\", err)\n\t}\n\n\treturn stdout.String(), stderr.String(), nil\n}\n\n// SaveLog saves the current stdout log of the container to a path\n// on the host system.\nfunc (t *HeadscaleInContainer) SaveLog(path string) (string, string, error) {\n\treturn dockertestutil.SaveLog(t.pool, t.container, path)\n}\n\nfunc (t *HeadscaleInContainer) SaveMetrics(savePath string) error {\n\treq, err := http.NewRequestWithContext(context.Background(), http.MethodGet, \"http://\"+net.JoinHostPort(t.hostname, \"9090\")+\"/metrics\", nil)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"creating metrics request: %w\", err)\n\t}\n\n\tresp, err := http.DefaultClient.Do(req)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"getting metrics: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\tout, err := os.Create(savePath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"creating file for metrics: %w\", err)\n\t}\n\tdefer out.Close()\n\n\t_, err = io.Copy(out, resp.Body)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"copy response to file: %w\", err)\n\t}","sourceCodeStart":724,"sourceCodeEnd":760,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/integration/hsic/hsic.go#L724-L760","documentation":"Returned by HeadscaleInContainer.SaveMetrics when constructing the HTTP GET request to http://<container-host>:9090/metrics fails. http.NewRequestWithContext only errors on an invalid method or an unparsable URL, so in practice this fires when the container hostname/port produce a malformed URL.","triggerScenarios":"Calling SaveMetrics(savePath) when t.hostname is empty or contains characters that make \"http://\"+net.JoinHostPort(t.hostname, \"9090\")+\"/metrics\" an invalid URL.","commonSituations":"HeadscaleInContainer was constructed without a resolvable hostname; the container failed to start and hostname was never set; a custom option overrode the hostname with an invalid value.","solutions":["Ensure the headscale container was created and started successfully before calling SaveMetrics (wait for WaitForRunning)","Check that a custom hsic.WithHostname option, if used, supplies a valid DNS name","Inspect the wrapped error — url.Error will name the malformed URL part"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if headscale.GetHostname() == \"\" {\n    return errors.New(\"headscale hostname not set; container not ready for metrics scrape\")\n}","typeGuard":null,"tryCatchPattern":"if err := headscale.SaveMetrics(path); err != nil {\n    t.Logf(\"metrics not saved: %v\", err) // metrics capture is best-effort diagnostics\n}","preventionTips":["Only call SaveMetrics after WaitForRunning succeeds","Supply a valid hostname when using WithHostname"],"tags":["metrics","http","integration-test","validation"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}