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
- Check the wrapped error's status: 401/403 → fix token and scopes (api), 404 → fix project path, 400/422 → fix title/labels payload
- Verify token validity: curl -H "PRIVATE-TOKEN: ..." /api/v4/user
- Confirm projectPath matches the GitLab project (namespace/name, URL-encoded)
- 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
- Use a token with api scope and verify project write access before creating issues
- Validate title/labels against GitLab limits before calling
- URL-encode the project path correctly in client config
- Handle 429 with exponential backoff
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
- failed to update issue: %w
- failed to get issue links: %w
- request failed (attempt %d/%d): %w
- failed to read response (attempt %d/%d): %w
- API error: %s (status %d)
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/6662418404938c03.
Report an issue: GitHub.