{"record":{"id":"4032d614aa53d3e0","repo":"wtfutil/wtf","slug":"errors-new-msg-msg","errorCode":null,"errorMessage":"errors.New(msg.Msg)","messagePattern":"errors\\.New\\(msg\\.Msg\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/grafana/client.go","lineNumber":103,"sourceCode":"\tif client.apiKey != \"\" {\n\t\treq.Header.Add(\"Authorization\", fmt.Sprintf(\"Bearer %s\", client.apiKey))\n\t}\n\n\tres, err := http.DefaultClient.Do(req)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer func() { _ = res.Body.Close() }()\n\n\tif res.StatusCode != 200 {\n\t\tmsg := struct {\n\t\t\tMsg string `json:\"message\"`\n\t\t}{}\n\t\terr = utils.ParseJSON(&msg, res.Body)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn nil, errors.New(msg.Msg)\n\t}\n\n\tvar out []Alert\n\terr = utils.ParseJSON(&out, res.Body)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsort.SliceStable(out, func(i, j int) bool {\n\t\treturn out[i].State < out[j].State\n\t})\n\n\treturn out, nil\n}\n","sourceCodeStart":85,"sourceCodeEnd":118,"githubUrl":"https://github.com/wtfutil/wtf/blob/bb838c1ccb0f0f3223690df44afdec663d622881/modules/grafana/client.go#L85-L118","documentation":"grafana client.Alerts() parses the API response; when Grafana returns a non-OK status the body is decoded for its 'message' field and that message is surfaced as a Go error via errors.New(msg.Msg). The text you see is Grafana's own API error message passed through verbatim (it may be empty if the body lacks 'message').","triggerScenarios":"Grafana API returning 401/403 (bad or missing API key), 404 (wrong path/apiURL or alert endpoints disabled), 4xx/5xx from a proxy; /api/alerts endpoint unavailable in newer Grafana versions (unified alerting moved to /api/v1/provisioning or /api/alertmanager).","commonSituations":"Grafana v9+ where legacy /api/alerts returns an error for unified alerting users; expired API key; reversed-proxy auth wall returning an HTML error page; wrong org/API key without alert read permissions.","solutions":["Verify the Grafana API key (Viewer+ role, correct org) and apiURL in the widget config","Confirm your Grafana version's alerting API path and update the module or Grafana to a compatible version","Test the endpoint manually: curl -H 'Authorization: Bearer <key>' <apiURL>/api/alerts","Check the returned message text — it names the specific auth/permission/path problem"],"exampleFix":"# before\ncurl -H \"Authorization: Bearer expiredkey\" https://grafana/api/alerts  -> 401 invalid API key\n# after\ncurl -H \"Authorization: Bearer <valid service-account token>\" https://grafana/api/alerts","handlingStrategy":"try-catch","validationCode":"// verify API access before widget refresh\nresp, _ := http.Get(apiURL + \"/api/health\")\nif resp == nil || resp.StatusCode != 200 {\n    return errors.New(\"grafana unreachable or wrong apiURL\")\n}","typeGuard":null,"tryCatchPattern":"alerts, err := client.Alerts()\nif err != nil {\n    var apiErr *grafanaAPIError\n    if errors.As(err, &apiErr) {\n        log.Printf(\"grafana said: %s\", apiErr.Msg) // pass-through message\n    }\n    return err\n}","preventionTips":["Keep the API key valid with alert read permissions for the right org","Check endpoint compatibility with your Grafana version (legacy vs unified alerting)","curl the endpoint manually when the widget goes blank","Confirm the apiURL points at the Grafana origin, not an auth-walled proxy"],"tags":["go","api","grafana","http"],"backgroundTag":"upstream-api-error-response","analyzedSha":"bb838c1ccb0f0f3223690df44afdec663d622881","analyzedAt":"2026-09-03T17:02:45.030Z","contentChangedAt":"2026-09-03T17:02:45.030Z","schemaVersion":2},"datasetVersion":"2026-09-11T00:17:11.886Z"}