gastownhall/beads · error

Failed to update %s: %v

Error message

Failed to update %s: %v

What it means

In doPull (internal/tracker/engine.go:534), when pulling an issue that already exists locally, beads wraps the update in a storage transaction (RunInIssueLifecycleTransaction -> applyPullIssueUpdate) applying field updates and labels. If the transaction fails, the engine warns with the local issue ID, increments stats.Errors, and continues with the next issue. The sync is not aborted; the failed issue simply is not updated.

Source

Thrown at internal/tracker/engine.go:534

				e.msg("[dry-run] Would update local issue: %s - %s", extIssue.Identifier, ui.SanitizeForTerminal(extIssue.Title))
				stats.Updated++
			} else {
				e.msg("[dry-run] Would import: %s - %s", extIssue.Identifier, ui.SanitizeForTerminal(extIssue.Title))
				stats.Created++
			}
			continue
		}

		if existing != nil {
			updates := buildPullIssueUpdates(existing, conv.Issue, ref)
			if raw, ok := marshalTrackerMetadata(extIssue.Metadata); ok {
				updates["metadata"] = raw
			}

			if err := e.Store.RunInIssueLifecycleTransaction(ctx, fmt.Sprintf("bd: pull update %s", existing.ID), func(tx storage.IssueLifecycleTransaction) error {
				return applyPullIssueUpdate(ctx, tx, existing.ID, updates, conv.Issue.Labels, e.Actor)
			}); err != nil {
				e.warn("Failed to update %s: %v", existing.ID, err)
				stats.Errors++
				if pulledIDs != nil {
					pulledIDs[existing.ID] = true
				}
				continue
			}
			stats.Updated++
			if pulledIDs != nil {
				pulledIDs[existing.ID] = true
			}
		} else {
			// Create new issue
			conv.Issue.ExternalRef = strPtr(ref)
			if raw, ok := marshalTrackerMetadata(extIssue.Metadata); ok {
				conv.Issue.Metadata = raw
			}
			if err := e.Store.CreateIssue(ctx, conv.Issue, e.Actor); err != nil {
				e.warn("Failed to create issue for %s: %v", extIssue.Identifier, err)

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the `%v` detail to identify the failing constraint (lock, uniqueness, invalid field, schema).
  2. Ensure no other bd process holds the DB; retry the pull after concurrent commands finish.
  3. Fix the offending remote/local data (e.g. invalid status or metadata the local schema cannot accept).
  4. Run pending migrations / `bd doctor` if the schema may be out of date after an upgrade.
  5. Re-run the pull; issues whose updates failed are retried since they were not synced successfully.

Example fix

// before: concurrent process holds a write lock
Failed to update bd-123: database is locked (5) (SQLITE_BUSY)
// after: finish/close the concurrent bd process, then
bd sync  # update succeeds, stats.Updated incremented for bd-123
Defensive patterns

Strategy: retry

Validate before calling

// before running sync, ensure no other bd process holds the DB and it is writable:
bd doctor   # or: check no concurrent `bd` runs; test write access to the .beads database

Try / catch

// doPull continues past the failure; verify via returned stats and re-run:
stats, err := eng.Sync(ctx, opts)
if err != nil { log.Fatalf("sync: %v", err) }
if stats.Errors > 0 {
    time.Sleep(2 * time.Second)
    if _, err := eng.Sync(ctx, opts); err != nil { log.Printf("retry sync failed: %v", err) }
}

Prevention

When it happens

Trigger: Running a pull/sync where a remote issue maps to an existing local issue and the storage update fails — DB locked or closed, constraint violation (e.g. invalid status/type value, unparseable metadata JSON from the tracker), uniqueness conflict on external_ref or ID, or the lifecycle transaction rejects a field transition.

Common situations: Concurrent `bd` processes contending for the same database (lock timeouts); tracker metadata containing values that violate local storage constraints; schema mismatch after a beads version upgrade without migration; disk-full or corrupted Dolt/sqlite state.

Related errors


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