gastownhall/beads · error
work item %d: %w
Error message
work item %d: %w
What it means
Reconciler.checkSingleItem fetches an individual work item to verify it still exists/is accessible; any error that isn't an explicit 404 (Not Found → Deleted list) or 403 (Forbidden → Denied list) is recorded in result.Errors wrapped as 'work item <id>'. Reconcile continues checking other items and aggregates these errors.
Source
Thrown at internal/ado/reconcile.go:135
_, err := r.Client.FetchWorkItems(ctx, []int{id})
if err == nil {
return // Item exists and is accessible
}
idStr := strconv.Itoa(id)
var apiErr *APIError
if errors.As(err, &apiErr) {
switch apiErr.StatusCode {
case http.StatusNotFound:
result.Deleted = append(result.Deleted, idStr)
return
case http.StatusForbidden:
result.Denied = append(result.Denied, idStr)
return
}
}
result.Errors = append(result.Errors, fmt.Errorf("work item %d: %w", id, err))
}
func (r *Reconciler) getInterval(ctx context.Context) int {
val, err := r.Store.GetConfig(ctx, configReconcileInterval)
if err != nil || val == "" {
return DefaultReconcileInterval
}
n, err := strconv.Atoi(val)
if err != nil || n <= 0 {
return DefaultReconcileInterval
}
return n
}
func (r *Reconciler) getCounter(ctx context.Context) int {
val, err := r.Store.GetConfig(ctx, configSyncsSinceReconcile)
if err != nil || val == "" {
return 0View on GitHub (pinned to 71377f2769)
Solutions
- Unwrap the inner error and check its HTTP status; fix auth (rotate PAT) for 401.
- Check Azure DevOps service health if the status is 5xx, then re-run reconcile.
- Verify ado.org/ado.project configuration still points at the right org/project.
- Retry later for transient network errors — reconcile is safe to re-run.
Example fix
// before: every non-404/403 error is just collected
result.Errors = append(result.Errors, fmt.Errorf("work item %d: %w", id, err))
// after: classify auth errors for clearer reporting
var apiErr *APIError
if errors.As(err, &apiErr) && apiErr.StatusCode == http.StatusUnauthorized {
result.Errors = append(result.Errors, fmt.Errorf("work item %d: authentication failed, check ado.pat/AZURE_DEVOPS_PAT: %w", id, err))
return
}
result.Errors = append(result.Errors, fmt.Errorf("work item %d: %w", id, err)) Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-validate config/auth before reconcile
if os.Getenv("AZURE_DEVOPS_PAT") == "" && patConfigEmpty {
return fmt.Errorf("PAT not configured; reconcile would fail on every item")
} Type guard
func isAuthError(err error) bool {
var apiErr *APIError
return errors.As(err, &apiErr) &&
(apiErr.StatusCode == http.StatusUnauthorized || apiErr.StatusCode == http.StatusForbidden)
} Try / catch
result := reconciler.Reconcile(ctx)
for _, err := range result.Errors {
if isAuthError(err) {
return fmt.Errorf("reconcile aborted: fix credentials: %w", err)
}
log.Printf("item-level reconcile issue (continuing): %v", err)
} Prevention
- Rotate the PAT before expiry and update AZURE_DEVOPS_PAT in CI secrets.
- Monitor Azure DevOps service health during scheduled reconcile windows.
- Verify org/project config after any project rename.
- Schedule reconciles with retry/backoff to ride out transient 5xx.
When it happens
Trigger: GET of the work item during Reconcile fails with anything other than 404/403 — 401 from a bad/expired PAT, 500 from ADO, timeouts, DNS/network errors, or 400 from a malformed work item URL.
Common situations: Expired or revoked Azure DevOps PAT mid-sync; ADO service outage/5xx during reconciliation; corporate proxy breaking TLS; project renamed so the configured project no longer resolves.
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 list projects: %w
- remove relation %d: %w
- add link %s to %d: %w
- transitioning to %q: %w
- transitioning to intermediate state %q: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/1e5a73ff64abbb9e.
Report an issue: GitHub.