cli/cli · error
api call failed
Error message
api call failed
What it means
Generic non-2xx indicator in the garden command's HTTP helper (used for the commits/contributions animation data). After client.Do succeeds at the transport level, any status code outside 200-299 (other than the special-cased 204) collapses into this message with no status code or body attached, which is why it is unhelpfully vague.
Source
Thrown at pkg/cmd/repo/garden/http.go:98
// getResponse performs the API call and returns the response's link header values.
// If the "Link" header is missing, the returned slice will be nil.
func getResponse(client *http.Client, url safeurl.SafeURL, data interface{}) ([]string, error) {
req, err := http.NewRequest("GET", url.String(), nil)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json; charset=utf-8")
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
success := resp.StatusCode >= 200 && resp.StatusCode < 300
if !success {
return nil, errors.New("api call failed")
}
links := resp.Header["Link"]
if resp.StatusCode == http.StatusNoContent {
return links, nil
}
b, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
err = json.Unmarshal(b, &data)
if err != nil {
return nil, err
}
View on GitHub (pinned to 0eeec0b92e)
Solutions
- Check auth first: gh auth status; refresh if expired
- Reproduce the failure with a data-rich command: gh repo view <repo> --json name, or gh api /repos/OWNER/REPO to see the real status and message
- Wait and retry if you were rate limited (check remaining quota: gh api rate_limit)
- If you are developing against this helper, replace the bare error with api.HandleHTTPError(resp) or fmt.Errorf including resp.StatusCode and the body
Example fix
// before
if !success {
return nil, errors.New("api call failed")
}
// after
if !success {
return nil, fmt.Errorf("api call failed: %s", resp.Status)
} Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-flight the same endpoint with full error reporting:
if _, err := ghapi("GET", "/repos/"+owner+"/"+repo); err != nil {
return fmt.Errorf("garden prerequisites failed: %w", err)
} Try / catch
links, err := gardenHTTP(client, url)
if err != nil && err.Error() == "api call failed" {
// the helper discards status/body; re-check auth and rate limits:
_ = run("gh", "auth", "status")
return fmt.Errorf("garden API call failed; check gh auth status and rate limits")
} Prevention
- Keep credentials fresh: gh auth status before novelty/interactive commands
- If you maintain code calling this helper, include resp.StatusCode in the error
- Watch rate limits (gh api rate_limit) when the animation polls repeatedly
When it happens
Trigger: The garden GraphQL/REST call returning 401 (bad token), 403 (rate limit/SAML), 404 (repo not found), or 5xx. The response body, which contains the actual reason, is discarded; only the Link header is kept for pagination.
Common situations: Expired gh credentials, hitting a secondary rate limit while the animation polls, private repos invisible to the token, or GitHub incidents. Users see only 'api call failed' and cannot tell which of these it is.
Related errors
- could not fetch tree %s: %w
- failed to list sessions: %s
- failed to get session: %s
- ErrNotOnAnyBranch
- could not find key "oauth_token"
AI-assisted analysis of cli/cli@0eeec0b92e (2026-08-15).
Data as JSON: /api/errors/36eead2f34098f79.
Report an issue: GitHub.