gastownhall/beads · error
failed to update issue: %w
Error message
failed to update issue: %w
What it means
UpdateIssue failed at the HTTP layer when PUTting updates to an existing issue identified by IID. The cause is wrapped from doRequest.
Source
Thrown at internal/gitlab/client.go:369
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)
}
var issue Issue
if err := json.Unmarshal(respBody, &issue); err != nil {
return nil, fmt.Errorf("failed to parse update response: %w", err)
}
return &issue, nil
}
// GetIssueLinks retrieves issue links for the specified issue IID.
func (c *Client) GetIssueLinks(ctx context.Context, iid int) ([]IssueLink, error) {
urlStr := c.buildURL("/projects/"+c.projectPath()+"/issues/"+strconv.Itoa(iid)+"/links", nil)
respBody, _, err := c.doRequest(ctx, http.MethodGet, urlStr, nil)
if err != nil {
return nil, fmt.Errorf("failed to get issue links: %w", err)
}
View on GitHub (pinned to 71377f2769)
Solutions
- Verify the IID exists in the configured project (FetchIssueByIID first)
- Check wrapped status: 404 → fix projectPath/iid, 403 → token scope, 400/422 → fix update keys/values
- Use valid state_event values ("close", "reopen") and correct label formats
- Check issue confidentiality/lock settings if 403 persists
Example fix
// before
client.UpdateIssue(ctx, 123, map[string]interface{}{"state": "closed"}) // invalid key
// after
client.UpdateIssue(ctx, 123, map[string]interface{}{"state_event": "close"}) Defensive patterns
Strategy: validation
Validate before calling
if iid <= 0 { return errors.New("invalid iid") }
if _, err := client.FetchIssueByIID(ctx, iid); err != nil {
return fmt.Errorf("iid %d not found in configured project: %w", iid, err)
} Type guard
func isUpdateRejected(err error) bool {
return strings.Contains(err.Error(), "failed to update issue")
} Try / catch
issue, err := client.UpdateIssue(ctx, iid, updates)
if err != nil {
if strings.Contains(err.Error(), "404") { return fmt.Errorf("issue iid %d not found: %w", iid, err) }
if strings.Contains(err.Error(), "403") { return fmt.Errorf("token lacks write access: %w", err) }
return err
} Prevention
- Validate update keys against the GitLab issues API (state_event, not state)
- Use iids scoped to the configured project only
- Ensure token has api scope for writes
- Fetch the issue first to confirm existence and permissions
When it happens
Trigger: PUT /projects/:id/issues/:iid fails: 404 (IID not found in the configured project or project path wrong), 400/422 invalid update keys or values, 401/403 token lacks write access, or network/context error.
Common situations: Passing an IID from a different project, sending update keys GitLab rejects (e.g. bad state_event, malformed labels), token without api scope, or updating a closed/locked issue without permission.
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 create 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/c44bebc452d6cf3b.
Report an issue: GitHub.