gastownhall/beads · error

fetch issue %s: %w

Error message

fetch issue %s: %w

What it means

FetchIssueTimestamp performs a GET to {apiBase}/issue/{key}?fields=updated and wraps any transport/HTTP error from doRequest as 'fetch issue %s: %w' with the Jira issue key. The wrapped cause carries the real reason (network failure, 401/403, 404, rate limit).

Source

Thrown at internal/jira/client.go:141

// apiBase returns the versioned REST API base URL, e.g. "https://host/rest/api/3".
func (c *Client) apiBase() string {
	v := c.APIVersion
	if v == "" {
		v = "3"
	}
	return c.URL + "/rest/api/" + v
}

// FetchIssueTimestamp fetches the updated timestamp for a single Jira issue.
func (c *Client) FetchIssueTimestamp(ctx context.Context, jiraKey string) (time.Time, error) {
	var zero time.Time

	apiURL := fmt.Sprintf("%s/issue/%s?fields=updated", c.apiBase(), url.PathEscape(jiraKey))

	body, err := c.doRequest(ctx, "GET", apiURL, nil)
	if err != nil {
		return zero, fmt.Errorf("fetch issue %s: %w", jiraKey, err)
	}

	var result struct {
		Fields struct {
			Updated string `json:"updated"`
		} `json:"fields"`
	}

	if err := json.Unmarshal(body, &result); err != nil {
		return zero, fmt.Errorf("parse Jira response: %w", err)
	}

	updated, err := ParseTimestamp(result.Fields.Updated)
	if err != nil {
		return zero, fmt.Errorf("parse Jira timestamp: %w", err)
	}

	return updated, nil

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped cause: fix auth (token/credentials) if 401/403, or the URL/base if 404
  2. Verify network connectivity and VPN access to the Jira host
  3. Confirm the issue key is correct and the issue still exists in Jira
  4. Retry on transient failures (429/5xx), honoring Jira rate-limit headers

Example fix

// before
t, err := client.FetchIssueTimestamp(ctx, "WRONG-999")
// after
// verify key exists, then handle/retry
t, err := client.FetchIssueTimestamp(ctx, "PROJ-123")
if err != nil {
    var retriable = errors.Is(err, context.DeadlineExceeded) // inspect cause
    _ = retriable
}
Defensive patterns

Strategy: retry

Validate before calling

// verify reachability before calling
resp, err := http.Get(jiraBase + "/rest/api/2/myself")
if err != nil || resp.StatusCode >= 400 { /* fix auth/url first */ }

Try / catch

t, err := client.FetchIssueTimestamp(ctx, key)
if err != nil {
    var we interface{ Temporary() bool }
    if errors.As(err, &we) && we.Temporary() {
        // retry with backoff
    }
    // else: auth/404 — do not retry, fix credentials or key
}

Prevention

When it happens

Trigger: Calling FetchIssueTimestamp when the Jira instance is unreachable, credentials are invalid, the issue key does not exist (or PathEscape-mangled key is malformed), or the API base URL is wrong.

Common situations: Expired or rotated Jira API tokens; VPN/network outage; wrong jiraBase configured so apiBase points to a non-existent REST path; issue deleted or key typo'd; Jira rate limiting (429).

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/cf35d377442fbd28. Report an issue: GitHub.