gastownhall/beads · error

failed to create issue: %w

Error message

failed to create issue: %w

What it means

CreateIssue failed at the HTTP layer when POSTing the new issue to GitLab. The library wraps the doRequest error, so the cause (auth, validation, network) is in the wrapped error.

Source

Thrown at internal/gitlab/client.go:353

	}

	return filterByProject(allIssues, filter), nil
}

// CreateIssue creates a new issue in GitLab.
func (c *Client) CreateIssue(ctx context.Context, title, description string, labels []string) (*Issue, error) {
	body := map[string]interface{}{
		"title":       title,
		"description": description,
	}
	if len(labels) > 0 {
		body["labels"] = labels
	}

	urlStr := c.buildURL("/projects/"+c.projectPath()+"/issues", nil)
	respBody, _, err := c.doRequest(ctx, http.MethodPost, urlStr, body)
	if err != nil {
		return nil, fmt.Errorf("failed to create issue: %w", err)
	}

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

	return &issue, nil
}

// UpdateIssue updates an existing issue in GitLab.
func (c *Client) UpdateIssue(ctx context.Context, iid int, updates map[string]interface{}) (*Issue, error) {
	urlStr := c.buildURL("/projects/"+c.projectPath()+"/issues/"+strconv.Itoa(iid), nil)
	respBody, _, err := c.doRequest(ctx, http.MethodPut, urlStr, updates)
	if err != nil {
		return nil, fmt.Errorf("failed to update issue: %w", err)
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check the wrapped error's status: 401/403 → fix token and scopes (api), 404 → fix project path, 400/422 → fix title/labels payload
  2. Verify token validity: curl -H "PRIVATE-TOKEN: ..." /api/v4/user
  3. Confirm projectPath matches the GitLab project (namespace/name, URL-encoded)
  4. Retry with backoff if 429/5xx

Example fix

// before
client.CreateIssue(ctx, "", "description", nil) // empty title -> 422
// after
if title == "" { return errors.New("title required") }
client.CreateIssue(ctx, title, description, labels)
Defensive patterns

Strategy: validation

Validate before calling

if strings.TrimSpace(title) == "" { return errors.New("issue title is required") }
if len(description) > 1000000 { return errors.New("description too long") }
// verify token write access
resp, _ := http.Get(baseURL + "/api/v4/user") // 200 means token valid

Type guard

func isCreateRejected(err error) bool {
	s := err.Error()
	return strings.Contains(s, "failed to create issue")
}

Try / catch

issue, err := client.CreateIssue(ctx, title, desc, labels)
if err != nil {
	switch {
	case strings.Contains(err.Error(), "401"), strings.Contains(err.Error(), "403"):
		return fmt.Errorf("check token scopes: %w", err)
	case strings.Contains(err.Error(), "429"):
		time.Sleep(time.Second); // retry
	default:
		return err
	}
}

Prevention

When it happens

Trigger: POST /projects/:id/issues fails: 401 bad token, 403 insufficient scope, 400/422 validation (empty title, description too long, invalid label), 404 wrong project path, or network failure.

Common situations: Token lacking api/write scope, project path misconfigured (URL-encoded path wrong), title empty or exceeding limits, spam/CAPTCHA protection on the project, or hitting 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/6662418404938c03. Report an issue: GitHub.