{"record":{"id":"c3ce901456933603","repo":"gastownhall/beads","slug":"create-issue-w","errorCode":null,"errorMessage":"create issue: %w","messagePattern":"create issue: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/jira/client.go","lineNumber":265,"sourceCode":"\t}\n\n\treturn &issue, nil\n}\n\n// CreateIssue creates a new issue in Jira.\n// fields should include \"project\", \"summary\", \"issuetype\", and optionally other fields.\nfunc (c *Client) CreateIssue(ctx context.Context, fields map[string]interface{}) (*Issue, error) {\n\tpayload := map[string]interface{}{\"fields\": fields}\n\tdata, err := json.Marshal(payload)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"marshal create request: %w\", err)\n\t}\n\n\tapiURL := fmt.Sprintf(\"%s/issue\", c.apiBase())\n\n\tbody, err := c.doRequest(ctx, \"POST\", apiURL, data)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"create issue: %w\", err)\n\t}\n\n\t// Create response only returns id, key, self. Fetch the full issue.\n\tvar created struct {\n\t\tID   string `json:\"id\"`\n\t\tKey  string `json:\"key\"`\n\t\tSelf string `json:\"self\"`\n\t}\n\tif err := json.Unmarshal(body, &created); err != nil {\n\t\treturn nil, fmt.Errorf(\"parse create response: %w\", err)\n\t}\n\n\treturn c.GetIssue(ctx, created.Key)\n}\n\n// UpdateIssue updates an existing Jira issue by key.\nfunc (c *Client) UpdateIssue(ctx context.Context, key string, fields map[string]interface{}) error {\n\tpayload := map[string]interface{}{\"fields\": fields}","sourceCodeStart":247,"sourceCodeEnd":283,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/jira/client.go#L247-L283","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check the wrapped error's HTTP status and Jira error messages: 400 responses list which fields are invalid.","Verify project key and issuetype name exactly match the instance (query createmeta if unsure).","For 401/403, fix the API token and confirm the account has Create Issues permission in the target project.","If 429, add backoff/jitter between creations.","Note the issue may have been created despite later errors — check Jira before retrying blindly."],"exampleFix":"// before\nissue, err := client.CreateIssue(ctx, map[string]interface{}{\n    \"project\": map[string]string{\"key\": \"PROJ\"},\n    \"summary\": \"t\", // missing issuetype -> 400\n})\n// after\nissue, err := client.CreateIssue(ctx, map[string]interface{}{\n    \"project\":   map[string]string{\"key\": \"PROJ\"},\n    \"summary\":   \"Fix login failure\",\n    \"issuetype\": map[string]string{\"name\": \"Bug\"},\n})","handlingStrategy":"validation","validationCode":"// Validate required fields and permissions before POST:\nfunc validCreateFields(fields map[string]interface{}) error {\n    for _, req := range []string{\"project\", \"summary\", \"issuetype\"} {\n        if _, ok := fields[req]; !ok {\n            return fmt.Errorf(\"missing required field %q\", req)\n        }\n    }\n    return nil\n}\n// Preflight: GET /rest/api/3/createmeta?projectKeys=PROJ to confirm issuetype names","typeGuard":"func isPermissionDenied(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"403\")\n}","tryCatchPattern":"issue, err := client.CreateIssue(ctx, fields)\nif err != nil {\n    if strings.Contains(err.Error(), \"400\") {\n        // Jira's error body lists invalid fields; log and fix fields before retry\n        log.Printf(\"create rejected: %v — check project key, issuetype name, custom fields\", err)\n        return\n    }\n    if isPermissionDenied(err) {\n        return fmt.Errorf(\"account lacks create permission in project: %w\", err)\n    }\n    // 429/5xx/timeouts: retry with backoff, but check for partial creation first\n    return retryWithBackoff(func() error { _, err = client.CreateIssue(ctx, fields); return err })\n}","preventionTips":["Always include project, summary, and issuetype; verify names against createmeta","Grant the integration account Create Issues permission in target projects","Add backoff/jitter for bulk creation to avoid 429s","Before retrying after unknown errors, search Jira for the summary to avoid duplicates"],"tags":["jira","http","validation","go"],"backgroundTag":"http-request-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}