{"record":{"id":"ff8ffc762f749878","repo":"wtfutil/wtf","slug":"s-ff8ffc","errorCode":null,"errorMessage":"%s","messagePattern":"%s","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/victorops/client.go","lineNumber":39,"sourceCode":"\nfunc victorOpsRequest(url string, apiID string, apiKey string) ([]OnCallTeam, error) {\n\treq, err := http.NewRequest(\"GET\", url, http.NoBody)\n\tif err != nil {\n\t\tlogger.Log(fmt.Sprintf(\"Failed to initialize sessions to VictorOps. ERROR: %s\", err))\n\t\treturn nil, err\n\t}\n\n\treq.Header.Set(\"X-VO-Api-Id\", apiID)\n\treq.Header.Set(\"X-VO-Api-Key\", apiKey)\n\tclient := &http.Client{}\n\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\tlogger.Log(fmt.Sprintf(\"Failed to make request to VictorOps. ERROR: %s\", err))\n\t\treturn nil, err\n\t}\n\tif resp.StatusCode != 200 {\n\t\treturn nil, fmt.Errorf(\"%s\", resp.Status)\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\n\tresponse := &OnCallResponse{}\n\tif err := json.NewDecoder(resp.Body).Decode(response); err != nil {\n\t\tlogger.Log(fmt.Sprintf(\"Failed to decode JSON response. ERROR: %s\", err))\n\t\treturn nil, err\n\t}\n\n\tteams := parseTeams(response)\n\treturn teams, nil\n}\n\nfunc parseTeams(input *OnCallResponse) []OnCallTeam {\n\tvar teamResults []OnCallTeam\n\n\tfor _, data := range input.TeamsOnCall {\n\t\tvar team OnCallTeam","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/wtfutil/wtf/blob/bb838c1ccb0f0f3223690df44afdec663d622881/modules/victorops/client.go#L21-L57","documentation":"victorOpsRequest treats any non-200 HTTP status from the VictorOps (Splunk On-Call) API as an error and returns fmt.Errorf(\"%s\", resp.Status), so the message is the raw HTTP status line (e.g. '401 Unauthorized'). The library has no retry or body-based error extraction for this path — the status line is all you get.","triggerScenarios":"Fetch calls victorOpsRequest, client.Do succeeds, but the VictorOps API responds with a status other than 200: 401/403 for a bad or missing API key, 404 for a bad slug/routing key, 5xx for service problems.","commonSituations":"Expired or wrong apikey in config, org slug typo'd, VictorOps outage or rate limiting, account migrated to Splunk On-Call with revoked keys.","solutions":["Check the HTTP status in the message and fix accordingly: 401/403 → verify the apikey config value against the VictorOps REST API key","Verify the organization slug / routing key in the widget config matches your VictorOps account","Hit the API endpoint manually with curl using the same key to confirm the credential works","If 5xx, retry later or check the Splunk On-Call status page for incidents"],"exampleFix":"# before\napikey: \"old-revoked-key\"\n# after\napikey: \"current-key-from-victorops-ui\"","handlingStrategy":"retry","validationCode":"// preflight: verify the key works before relying on the widget\ncurl -s -o /dev/null -w \"%{http_code}\" -H \"X-VO-Api-Key: $KEY\" https://alert.victorops.com/api/public/v1/team","typeGuard":null,"tryCatchPattern":"data, err := w.Fetch()\nif err != nil {\n    if strings.Contains(err.Error(), \"401\") || strings.Contains(err.Error(), \"403\") {\n        log.Printf(\"victorops auth failure, check apikey: %v\", err)\n    } else if s := err.Error(); strings.Contains(s, \"5\") { // 5xx\n        time.Sleep(retryBackoff)\n        data, err = w.Fetch()\n    }\n}","preventionTips":["Rotate and verify the VictorOps API key before it expires","Monitor Splunk On-Call status for 5xx windows","Alert on auth-status failures distinctly from transient 5xx"],"tags":["go","http","victorops","api-error"],"backgroundTag":"http-non-200-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"}