gastownhall/beads · error
failed to update milestone: %w
Error message
failed to update milestone: %w
What it means
UpdateMilestone PUTs to /projects/:id/milestones/:id through doRequest, which handles authentication, retries, and HTTP error statuses. This error wraps any doRequest failure — network errors, timeouts, context cancellation, or HTTP errors such as 404 (bad milestoneID), 400 (invalid update fields), or 401/403. It is raised before JSON parsing, so the update never completed.
Source
Thrown at internal/gitlab/client.go:487
respBody, _, err := c.doRequest(ctx, http.MethodPost, urlStr, body)
if err != nil {
return nil, fmt.Errorf("failed to create milestone: %w", err)
}
var milestone Milestone
if err := json.Unmarshal(respBody, &milestone); err != nil {
return nil, fmt.Errorf("failed to parse milestone response: %w", err)
}
return &milestone, nil
}
// UpdateMilestone updates an existing milestone in GitLab.
func (c *Client) UpdateMilestone(ctx context.Context, milestoneID int, updates map[string]interface{}) (*Milestone, error) {
urlStr := c.buildURL("/projects/"+c.projectPath()+"/milestones/"+strconv.Itoa(milestoneID), nil)
respBody, _, err := c.doRequest(ctx, http.MethodPut, urlStr, updates)
if err != nil {
return nil, fmt.Errorf("failed to update milestone: %w", err)
}
var milestone Milestone
if err := json.Unmarshal(respBody, &milestone); err != nil {
return nil, fmt.Errorf("failed to parse milestone response: %w", err)
}
return &milestone, nil
}
// GraphQL support for work item hierarchy (Issue → Task parent-child).
// graphqlRequest executes a GraphQL query against the GitLab instance.
func (c *Client) graphqlRequest(ctx context.Context, query string, variables map[string]interface{}) (json.RawMessage, error) {
body := map[string]interface{}{"query": query}
if len(variables) > 0 {
body["variables"] = variables
}View on GitHub (pinned to 71377f2769)
Solutions
- Unwrap the error to read the HTTP status; 404 means verify the milestoneID belongs to the configured project
- Validate the updates map keys against GitLab's milestone update API (title, description, state_event, due_date, start_date)
- Ensure the token has api scope and sufficient role to edit milestones
- Retry with backoff for 5xx/transport errors after confirming connectivity to the GitLab host
Example fix
// before
m, err := client.UpdateMilestone(ctx, id, map[string]interface{}{"status": "done"}) // invalid key
// after
updates := map[string]interface{}{"state_event": "close"} // valid GitLab field
m, err := client.UpdateMilestone(ctx, id, updates)
if err != nil { return fmt.Errorf("update milestone %d: %w", id, err) } Defensive patterns
Strategy: validation
Validate before calling
// Whitelist updatable fields and require a known milestone ID
var allowed = map[string]bool{"title": true, "description": true, "due_date": true, "start_date": true, "state_event": true}
for k := range updates {
if !allowed[k] {
return fmt.Errorf("unsupported milestone update field %q", k)
}
}
if milestoneID <= 0 {
return fmt.Errorf("invalid milestone ID %d", milestoneID)
} Type guard
func isNotFound(err error) bool {
return strings.Contains(err.Error(), "404")
} Try / catch
m, err := client.UpdateMilestone(ctx, id, updates)
if err != nil {
if isNotFound(err) {
return fmt.Errorf("milestone %d not in this project: %w", id, err) // don't retry
}
if isTransient(err) {
return updateWithRetry(ctx, id, updates)
}
return err
} Prevention
- Resolve milestone IDs in the same project you configured on the client
- Restrict the updates map to documented GitLab milestone fields (e.g. state_event: close)
- Use tokens with api scope and edit permission on milestones
- Retry only transient 5xx/transport errors with backoff; fail fast on 4xx
When it happens
Trigger: Calling Client.UpdateMilestone with a milestoneID that does not exist in the configured project (404), invalid update keys/values (400), a token lacking write access (401/403), network outage, or a cancelled context.
Common situations: Using a milestone ID from a different project; sending unsupported field names in the updates map; token without api scope; GitLab maintenance windows causing 5xx; CI environments without network egress.
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 milestone: %w
- failed to fetch milestones: %w
- failed to fetch milestone by IID %d: %w
- request failed (attempt %d/%d): %w
- failed to fetch issue %d: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/0adfea826ada36e2.
Report an issue: GitHub.