{"record":{"id":"c4ace1840585e660","repo":"wtfutil/wtf","slug":"s-c4ace1","errorCode":null,"errorMessage":"%s","messagePattern":"%s","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"modules/pivotal/client.go","lineNumber":76,"sourceCode":"\treq.Header.Add(\"Content-Type\", \"application/json\")\n\treq.Header.Add(\"X-TrackerToken\", apiToken)\n\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\n\tdata, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// check if we received a Pivotal Error response\n\tErr := Error{}\n\terr = json.Unmarshal([]byte(string(data)), &Err)\n\tif err == nil && Err.Error != \"\" {\n\t\treturn nil, fmt.Errorf(\"%s\", Err.Error)\n\t}\n\n\treturn &Resource{Response: &resp, Raw: string(data)}, nil\n}\n\nfunc (pivotal *PivotalClient) getCurrentUser() (*User, error) {\n\tresource, err := pivotal.apiv5(\"me\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tuser := User{}\n\terr = json.Unmarshal([]byte(resource.Raw), &user)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &user, nil\n}","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/wtfutil/wtf/blob/bb838c1ccb0f0f3223690df44afdec663d622881/modules/pivotal/client.go#L58-L94","documentation":"The Pivotal client's apiv5 helper unmarshals every response body into an Error struct; if the payload contains a non-empty 'error' field, the client returns that server-provided message verbatim as a Go error. The displayed message is whatever Pivotal Tracker sent.","triggerScenarios":"json.Unmarshal succeeds and Err.Error != \"\" in apiv5 — i.e. Pivotal Tracker responded with an error payload instead of the expected resource, during getCurrentUser or searchStories calls.","commonSituations":"Invalid or expired X-TrackerToken API key (401 body), malformed search query parameters (400 with description), requesting a project id the token cannot access (403), or rate limiting responses.","solutions":["Regenerate the Pivotal Tracker API token and update it in the client configuration","Check the request parameters (project id, search query) — the error text itself names the invalid field","Verify the token has access to the requested project/scope","Inspect HTTP status alongside the message to distinguish auth vs validation vs rate-limit issues"],"exampleFix":"// before\ntoken: \"stale-token\"\n// after: generate at pivotaltracker.com/profile\ntoken: \"<fresh X-TrackerToken value>\"","handlingStrategy":"try-catch","validationCode":"req, _ := http.NewRequest(\"GET\", \"https://www.pivotaltracker.com/services/v5/me\", nil)\nreq.Header.Set(\"X-TrackerToken\", token)\nresp, err := http.DefaultClient.Do(req)\nif err == nil && resp.StatusCode == 401 {\n    log.Println(\"Pivotal API token invalid or expired\")\n}","typeGuard":"func pivotalAPIError(err error) (string, bool) {\n    var msg string\n    if err == nil {\n        return \"\", false\n    }\n    msg = err.Error()\n    return msg, msg != \"\"\n}","tryCatchPattern":"user, err := pivotal.getCurrentUser()\nif err != nil {\n    var apiErr *pivotal.Error\n    if errors.As(err, &apiErr) {\n        log.Printf(\"Pivotal rejected the request: %v\", err)\n    } else {\n        return err\n    }\n}","preventionTips":["Regenerate and rotate the X-TrackerToken API key regularly","Validate project IDs and search parameters before calling the API","Confirm token scope covers the projects being queried","Surface the server-provided error text to logs; Pivotal names the offending field"],"tags":["api-error","pivotal-tracker","authentication","http"],"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"}