wtfutil/wtf · error · Error

%s

Error message

%s

What it means

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.

Source

Thrown at modules/pivotal/client.go:76

	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("X-TrackerToken", apiToken)

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer func() { _ = resp.Body.Close() }()

	data, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	// check if we received a Pivotal Error response
	Err := Error{}
	err = json.Unmarshal([]byte(string(data)), &Err)
	if err == nil && Err.Error != "" {
		return nil, fmt.Errorf("%s", Err.Error)
	}

	return &Resource{Response: &resp, Raw: string(data)}, nil
}

func (pivotal *PivotalClient) getCurrentUser() (*User, error) {
	resource, err := pivotal.apiv5("me")
	if err != nil {
		return nil, err
	}

	user := User{}
	err = json.Unmarshal([]byte(resource.Raw), &user)
	if err != nil {
		return nil, err
	}
	return &user, nil
}

View on GitHub (pinned to bb838c1ccb)

Solutions

  1. Regenerate the Pivotal Tracker API token and update it in the client configuration
  2. Check the request parameters (project id, search query) — the error text itself names the invalid field
  3. Verify the token has access to the requested project/scope
  4. Inspect HTTP status alongside the message to distinguish auth vs validation vs rate-limit issues

Example fix

// before
token: "stale-token"
// after: generate at pivotaltracker.com/profile
token: "<fresh X-TrackerToken value>"
Defensive patterns

Strategy: try-catch

Validate before calling

req, _ := http.NewRequest("GET", "https://www.pivotaltracker.com/services/v5/me", nil)
req.Header.Set("X-TrackerToken", token)
resp, err := http.DefaultClient.Do(req)
if err == nil && resp.StatusCode == 401 {
    log.Println("Pivotal API token invalid or expired")
}

Type guard

func pivotalAPIError(err error) (string, bool) {
    var msg string
    if err == nil {
        return "", false
    }
    msg = err.Error()
    return msg, msg != ""
}

Try / catch

user, err := pivotal.getCurrentUser()
if err != nil {
    var apiErr *pivotal.Error
    if errors.As(err, &apiErr) {
        log.Printf("Pivotal rejected the request: %v", err)
    } else {
        return err
    }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of wtfutil/wtf@bb838c1ccb (2026-09-03). Data as JSON: /api/errors/c4ace1840585e660. Report an issue: GitHub.