{"record":{"id":"e88718aa5ac00e24","repo":"wtfutil/wtf","slug":"s-e88718","errorCode":null,"errorMessage":"%s","messagePattern":"%s","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/circleci/client.go","lineNumber":69,"sourceCode":"\turl := circleAPIURL.ResolveReference(&url.URL{Path: path, RawQuery: params.Encode()})\n\n\treq, err := http.NewRequest(\"GET\", url.String(), http.NoBody)\n\treq.Header.Add(\"Accept\", \"application/json\")\n\treq.Header.Add(\"Content-Type\", \"application/json\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\thttpClient := &http.Client{}\n\tresp, err := httpClient.Do(req)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tdefer func() { _ = resp.Body.Close() }()\n\n\tif resp.StatusCode < 200 || resp.StatusCode > 299 {\n\t\treturn nil, fmt.Errorf(\"%s\", resp.Status)\n\t}\n\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn body, nil\n}\n","sourceCodeStart":51,"sourceCodeEnd":78,"githubUrl":"https://github.com/wtfutil/wtf/blob/bb838c1ccb0f0f3223690df44afdec663d622881/modules/circleci/client.go#L51-L78","documentation":"CircleCI's circleRequest returns resp.Status verbatim (e.g. '404 Not Found') when the API responds with a non-2xx status. Like the Buildkite client, it surfaces the server's status line rather than a parsed API error body, so diagnosing requires mapping the status code to its cause.","triggerScenarios":"Any CircleCI API v2 call made by circleRequest (invoked from BuildsFor) returning <200 or >299 — invalid CIRCLECI_TOKEN (401), wrong project slug (404), malformed request (400), rate limiting or server errors (429/5xx).","commonSituations":"Expired CircleCI personal API token; project slug in the wrong 'vcs-slug/org/repo' form; token lacking scope for the project; CircleCI outage or rate limiting.","solutions":["Check the status code in the message: 401/403 → fix the API token, 404 → fix the project slug (vcs/org/repo)","Generate a fresh CircleCI personal API token with the needed scopes","Confirm the project slug format matches CircleCI v2 (e.g. 'gh/myorg/myrepo')","Retry with backoff for 429/5xx responses"],"exampleFix":"// before\nclient.baseURL = \"https://circleci.com/api/v2\"\nslug := \"myrepo\" // malformed → 404 Not Found\n// after\nslug := \"gh/myorg/myrepo\" // correct v2 project slug\nresp, err := client.circleRequest(\"/project/\" + slug + \"/builds\")","handlingStrategy":"try-catch","validationCode":"if os.Getenv(\"CIRCLECI_TOKEN\") == \"\" {\n    return errors.New(\"CIRCLECI_TOKEN must be set\")\n}\nif !strings.Contains(projectSlug, \"/\") {\n    return fmt.Errorf(\"project slug %q must be vcs/org/repo\", projectSlug)\n}","typeGuard":null,"tryCatchPattern":"builds, err := client.BuildsFor(projectSlug)\nif err != nil {\n    var statusErr error\n    switch {\n    case strings.Contains(err.Error(), \"401\"), strings.Contains(err.Error(), \"403\"):\n        statusErr = errors.New(\"circleci: check API token\")\n    case strings.Contains(err.Error(), \"404\"):\n        statusErr = fmt.Errorf(\"circleci: unknown project %q\", projectSlug)\n    default:\n        statusErr = err\n    }\n    return nil, statusErr\n}","preventionTips":["Use CircleCI v2 slug format (gh/org/repo or bb/org/repo)","Rotate personal API tokens before expiry","Retry transient 429/5xx with backoff"],"tags":["http","circleci","api","go"],"backgroundTag":"http-non-2xx-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"}