{"record":{"id":"351fffc9a3d45a8f","repo":"wtfutil/wtf","slug":"errors-new-string-body","errorCode":null,"errorMessage":"errors.New(string(body))","messagePattern":"errors\\.New\\(string\\(body\\)\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/uptimerobot/widget.go","lineNumber":174,"sourceCode":"\t)\n\n\tif errh != nil {\n\t\treturn nil, errh\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\n\tbody, _ := io.ReadAll(resp.Body)\n\n\t// First pass to read the status\n\tc := make(map[string]json.RawMessage)\n\terrj1 := json.Unmarshal(body, &c)\n\n\tif errj1 != nil {\n\t\treturn nil, errj1\n\t}\n\n\tif string(c[\"stat\"]) != `\"ok\"` {\n\t\treturn nil, errors.New(string(body))\n\t}\n\n\t// Second pass to get the actual info\n\tvar monitors []Monitor\n\terrj2 := json.Unmarshal(c[\"monitors\"], &monitors)\n\n\tif errj2 != nil {\n\t\treturn nil, errj2\n\t}\n\n\treturn monitors, nil\n}\n","sourceCodeStart":156,"sourceCodeEnd":187,"githubUrl":"https://github.com/wtfutil/wtf/blob/bb838c1ccb0f0f3223690df44afdec663d622881/modules/uptimerobot/widget.go#L156-L187","documentation":"Not a fixed message: getMonitors performs two JSON passes against the UptimeRobot API. After unmarshalling the response envelope it checks c[\"stat\"]; UptimeRobot replies with stat=\"ok\" on success. Anything else (stat=\"error\") means the API rejected the request, and the widget surfaces the entire raw response body via errors.New(string(body)) so the developer can see UptimeRobot's own error description.","triggerScenarios":"Widget.Refresh -> getMonitors: the first-pass JSON decode succeeds but c[\"stat\"] != \"\\\"ok\\\"\" — UptimeRobot returned an error payload such as invalid api_key, wrong monitor types parameter, or rate-limit exceeded; the whole HTTP body is embedded in the error.","commonSituations":"Invalid or revoked UptimeRobot API key (read-only vs full-access key mismatch); typo'd apiKey in settings.yml; exceeding UptimeRobot's API rate limits; UptimeRobot API v2 endpoint/parameter changes.","solutions":["Inspect the error text — it contains UptimeRobot's JSON error (type/message fields) explaining the cause.","Verify the apiKey in the uptimerobot module settings is a valid, active key (use a read-only key if only monitoring).","Check for rate limiting (message like 'exceed the calls limit') and reduce poll interval or wait.","Confirm you are using API v2 parameters; old v1 keys/params cause error responses."],"exampleFix":"// before: error body: {\"stat\":\"error\",\"error\":{\"type\":\"invalid_parameter\",\"message\":\"api_key is invalid\"}}\n// after (settings.yml)\nuptimerobot:\n  apiKey: \"u1234567-0123456789abcdef\"  # valid v2 API key","handlingStrategy":"validation","validationCode":"if apiKey == \"\" || !strings.HasPrefix(apiKey, \"u\") {\n    return errors.New(\"valid UptimeRobot API v2 key required\")\n}","typeGuard":null,"tryCatchPattern":"monitors, err := getMonitors(apiKey)\nif err != nil {\n    // err contains UptimeRobot's raw JSON body; parse it:\n    var apiErr struct {\n        Error struct{ Type, Message string } `json:\"error\"`\n    }\n    if json.Unmarshal([]byte(err.Error()), &apiErr) == nil {\n        log.Printf(\"uptimerobot: %s: %s\", apiErr.Error.Type, apiErr.Error.Message)\n    }\n}","preventionTips":["Test the API key with curl against https://api.uptimerobot.com/v2/getAccountDetails first.","Use a read-only key when only monitoring is needed.","Respect rate limits (10 req/min); tune poll interval accordingly."],"tags":["uptimerobot","api","dynamic-error","api-key"],"backgroundTag":"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"}