gastownhall/beads · error

get issue %s: %w

Error message

get issue %s: %w

What it means

GetIssue wraps errors from doRequest for GET /issue/{key}. The HTTP layer failed: non-2xx status, auth failure, timeout, or network error. The issue key is embedded in the message so callers know which issue fetch failed. Also surfaces indirectly through CreateIssue, which calls GetIssue to hydrate the newly created issue.

Source

Thrown at internal/jira/client.go:241

			startAt += len(result.Issues)
			continue
		}
		if result.IsLast || result.NextPageToken == "" {
			break
		}
		nextPageToken = result.NextPageToken
	}

	return allIssues, nil
}

// GetIssue fetches a single Jira issue by key (e.g., "PROJ-123").
func (c *Client) GetIssue(ctx context.Context, key string) (*Issue, error) {
	apiURL := fmt.Sprintf("%s/issue/%s?fields=%s", c.apiBase(), url.PathEscape(key), searchFields)

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

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

	return &issue, nil
}

// CreateIssue creates a new issue in Jira.
// fields should include "project", "summary", "issuetype", and optionally other fields.
func (c *Client) CreateIssue(ctx context.Context, fields map[string]interface{}) (*Issue, error) {
	payload := map[string]interface{}{"fields": fields}
	data, err := json.Marshal(payload)
	if err != nil {
		return nil, fmt.Errorf("marshal create request: %w", err)
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check the wrapped HTTP error's status: 404 means verify the issue key exists and is visible to your account.
  2. For 401/403, regenerate the API token and confirm permissions on the project.
  3. For 404 on CreateIssue follow-up, verify APIVersion is correct so apiBase matches your deployment.
  4. For timeouts/transient errors, retry the fetch.

Example fix

// before
issue, err := client.GetIssue(ctx, key)
// after: tolerate transient failures on hydration
issue, err := client.GetIssue(ctx, key)
if err != nil && isTransient(err) {
    issue, err = client.GetIssue(ctx, key)
}
Defensive patterns

Strategy: try-catch

Validate before calling

var jiraKeyRe = regexp.MustCompile(`^[A-Z][A-Z0-9]*-\d+$`)
func validIssueKey(k string) bool { return jiraKeyRe.MatchString(k) }
// Also preflight permissions:
// GET /rest/api/3/mypermissions?permissions=BROWSE_PROJECTS

Type guard

func isNotFound(err error) bool {
    return err != nil && strings.Contains(err.Error(), "404")
}

Try / catch

issue, err := client.GetIssue(ctx, key)
if err != nil {
    switch {
    case isNotFound(err):
        return nil, fmt.Errorf("issue %s does not exist or is not visible: %w", key, err)
    case errors.Is(err, context.DeadlineExceeded):
        // transient: safe to retry
        return client.GetIssue(ctx, key)
    default:
        return nil, err
    }
}

Prevention

When it happens

Trigger: Calling GetIssue (or CreateIssue completing then hydrating) with a key the server rejects: 404 for nonexistent key, 401/403 for auth or permission problems, 400 for malformed key, or transport failure.

Common situations: Typo'd issue key or wrong project prefix; API token revoked/expired; account lacks browse permission on the project; Jira Server instance lacking API v3; CreateIssue succeeding but the subsequent fetch hitting a transient error.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


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