gastownhall/beads · error

create issue: %w

Error message

create issue: %w

What it means

CreateIssue wraps doRequest errors for POST /issue. The issue-creation HTTP call failed: validation error (400), auth/permission failure (401/403), or transport problem. The library throws it so callers know no issue was created (or creation was rejected) before any hydration happens.

Source

Thrown at internal/jira/client.go:265

	}

	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)
	}

	apiURL := fmt.Sprintf("%s/issue", c.apiBase())

	body, err := c.doRequest(ctx, "POST", apiURL, data)
	if err != nil {
		return nil, fmt.Errorf("create issue: %w", err)
	}

	// Create response only returns id, key, self. Fetch the full issue.
	var created struct {
		ID   string `json:"id"`
		Key  string `json:"key"`
		Self string `json:"self"`
	}
	if err := json.Unmarshal(body, &created); err != nil {
		return nil, fmt.Errorf("parse create response: %w", err)
	}

	return c.GetIssue(ctx, created.Key)
}

// UpdateIssue updates an existing Jira issue by key.
func (c *Client) UpdateIssue(ctx context.Context, key string, fields map[string]interface{}) error {
	payload := map[string]interface{}{"fields": fields}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check the wrapped error's HTTP status and Jira error messages: 400 responses list which fields are invalid.
  2. Verify project key and issuetype name exactly match the instance (query createmeta if unsure).
  3. For 401/403, fix the API token and confirm the account has Create Issues permission in the target project.
  4. If 429, add backoff/jitter between creations.
  5. Note the issue may have been created despite later errors — check Jira before retrying blindly.

Example fix

// before
issue, err := client.CreateIssue(ctx, map[string]interface{}{
    "project": map[string]string{"key": "PROJ"},
    "summary": "t", // missing issuetype -> 400
})
// after
issue, err := client.CreateIssue(ctx, map[string]interface{}{
    "project":   map[string]string{"key": "PROJ"},
    "summary":   "Fix login failure",
    "issuetype": map[string]string{"name": "Bug"},
})
Defensive patterns

Strategy: validation

Validate before calling

// Validate required fields and permissions before POST:
func validCreateFields(fields map[string]interface{}) error {
    for _, req := range []string{"project", "summary", "issuetype"} {
        if _, ok := fields[req]; !ok {
            return fmt.Errorf("missing required field %q", req)
        }
    }
    return nil
}
// Preflight: GET /rest/api/3/createmeta?projectKeys=PROJ to confirm issuetype names

Type guard

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

Try / catch

issue, err := client.CreateIssue(ctx, fields)
if err != nil {
    if strings.Contains(err.Error(), "400") {
        // Jira's error body lists invalid fields; log and fix fields before retry
        log.Printf("create rejected: %v — check project key, issuetype name, custom fields", err)
        return
    }
    if isPermissionDenied(err) {
        return fmt.Errorf("account lacks create permission in project: %w", err)
    }
    // 429/5xx/timeouts: retry with backoff, but check for partial creation first
    return retryWithBackoff(func() error { _, err = client.CreateIssue(ctx, fields); return err })
}

Prevention

When it happens

Trigger: POST /issue rejected with 400 because required fields are missing/invalid (project key, issuetype name, summary), 401/403 from bad token or no create permission, 429 rate limiting, or network failure.

Common situations: Wrong project key or issuetype name (e.g. "Bug" vs "Task" vs localized names); account lacking create-issue permission in the project; expired API token; mandatory custom fields not supplied; bulk creation tripping rate limits.

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/c3ce901456933603. Report an issue: GitHub.