gastownhall/beads · error

Failed to update %s in %s: %v

Error message

Failed to update %s in %s: %v

What it means

During push, updating an issue that already exists in the external tracker failed. The engine logs a warning, increments stats.Errors, and continues with the next issue unless the failure is rate-limit related. This indicates the remote tracker rejected or could not process the update for this specific issue.

Source

Thrown at internal/tracker/engine.go:1152

						if e.PushHooks.ContentEqual(issue, extIssue) {
							// Remote already matches: record the hash so future
							// runs skip the fetch above, not just the update.
							e.recordPushHash(ctx, issue, extRef)
							stats.Skipped++
							continue
						}
					} else if !extIssue.UpdatedAt.Before(issue.UpdatedAt) {
						stats.Skipped++ // Default: external is same or newer
						continue
					}
				}
			}

			if _, err := e.Tracker.UpdateIssue(ctx, extID, pushIssue); err != nil {
				if isRateLimitExhausted(err) {
					return stats, fmt.Errorf("sync aborted: %w", err)
				}
				e.warn("Failed to update %s in %s: %v", issue.ID, e.Tracker.DisplayName(), err)
				stats.Errors++
				if isRateLimitedErr(err) {
					e.warnRateLimitAbort(err, len(issues)-stats.Created-stats.Updated-stats.Skipped-stats.Errors)
					return stats, nil
				}
				continue
			}
			// Remember what we just pushed so the next sync can skip the fetch.
			e.recordPushHash(ctx, issue, extRef)
			stats.Updated++
		} else {
			stats.Skipped++
		}
	}

	span.SetAttributes(
		attribute.Int("sync.created", stats.Created),
		attribute.Int("sync.updated", stats.Updated),

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped tracker error to identify cause (404 → remote deleted, 403 → permissions, 422 → validation).
  2. If the remote issue was deleted, either recreate it or clear the local external_ref (`bd update <id> --external-ref=""`) so push re-creates it.
  3. Fix field values that violate tracker constraints (truncate descriptions, valid status names).
  4. Renew/repair the API token and confirm project-level edit permissions.
  5. Re-run `bd sync` after fixing; only affected issues will retry since successful ones are hash-skipped.

Example fix

// before: stale external_ref pointing at a deleted remote issue
// after: clear the ref so the next push re-creates it
bd update bd-123 --external-ref=""
bd push
Defensive patterns

Strategy: retry

Validate before calling

// pre-flight: ensure the remote issue exists and is editable
extID := tracker.ExtractIdentifier(derefStr(issue.ExternalRef))
if extID == "" { skip(issue) }
if _, err := tracker.FetchIssue(ctx, extID); err != nil {
    log.Printf("remote for %s unreachable: %v", issue.ID, err)
}

Try / catch

// exponential backoff on rate-limited or transient update failures
for i := 0; i < 5; i++ {
    stats, err := engine.Sync(ctx, opts)
    if err == nil && stats.Errors == 0 { return nil }
    if isRateLimitedErr(err) || isTransientErr(err) {
        time.Sleep(time.Duration(1<<i) * time.Second)
        continue
    }
    return err
}

Prevention

When it happens

Trigger: e.Tracker.UpdateIssue(ctx, extID, pushIssue) returns an error for an issue with a valid external_ref — e.g. HTTP 4xx/5xx from the tracker API, validation rejection of field values, deleted remote issue, or (non-exhausted) rate limiting.

Common situations: Remote issue was deleted or moved in the tracker; auth token lacks edit permission on that project; description or field value violates tracker limits (e.g. too long); temporary 5xx or secondary rate limit from GitHub/Jira/Linear APIs.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/007cb11d1f5cd799. Report an issue: GitHub.