gastownhall/beads · error

capture dependency edges for rename

Error message

capture dependency edges for rename %s -> %s: %w

What it means

This error wraps a failure while snapshotting the dependency edges of an issue immediately before its ID is renamed inside a transaction. The library captures all edges referencing oldID first so the journal can replay the rename correctly; if reading those edges fails, the rename is aborted to avoid an unrecoverable journal entry. It is only raised when journaling is enabled (journalEnabled).

Solutions

  1. Check database connectivity and retry the rename; the transaction is rolled back so it is safe to retry.
  2. Verify the dependencies table schema matches the installed migration set (bd doctor / migrations up to date).
  3. If journaling is misconfigured, fix or temporarily disable the journal and re-run the rename.
  4. Inspect the wrapped error (%w) for the driver-level cause (lock timeout, IO error) and address that root cause.

Example fix

// before
err := store.UpdateIssueID(ctx, oldID, newID, issue, actor) // fails on flaky Dolt connection
// after
if err := pingStore(ctx, store); err != nil {
    return fmt.Errorf("store unavailable, retry rename later: %w", err)
}
err := store.UpdateIssueID(ctx, oldID, newID, issue, actor)
Defensive patterns

Strategy: try-catch

Validate before calling

if err := store.Ping(ctx); err != nil {
    return fmt.Errorf("storage unavailable before rename: %w", err)
}

Try / catch

if err := store.UpdateIssueID(ctx, oldID, newID, issue, actor); err != nil {
    if strings.Contains(err.Error(), "capture dependency edges for rename") {
        // journal/DB read failed; safe to retry after connectivity check
        return retryLater(err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling UpdateIssueIDInTx (or UpdateIssueID) while the Dolt/journal is enabled and dependencyEdgesForIssueIDsInTx fails: the underlying SELECT over the dependencies table errors (connection drop, table locked, schema mismatch).

Common situations: Database connectivity loss mid-rename; a migration left the dependencies table in an unexpected schema; concurrent Dolt push/pull locking the table; running against an aux/journal store that is temporarily unavailable.

Related errors


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

Appendix: source

Thrown at internal/storage/issueops/bulk_ops.go:250

	if err := RecomputeIsBlockedInTx(ctx, tx, affectedIssues, affectedWisps); err != nil {
		return int(rowsAffected), fmt.Errorf("recompute is_blocked after source-repo delete: %w", err)
	}

	return int(rowsAffected), nil
}

//nolint:gosec // G201: table names are hardcoded
func UpdateIssueIDInTx(ctx context.Context, tx *sql.Tx, oldID, newID string, issue *types.Issue, actor string) error {
	// Capture the edges under the OLD id before the rename rewrites them; they
	// are what the journal replays as a remove/re-add pair around the identity
	// change.
	var renameEdges []journalDependencyEdge
	if journalEnabled(ctx, tx) {
		var err error
		renameEdges, err = dependencyEdgesForIssueIDsInTx(ctx, tx, []string{oldID})
		if err != nil {
			return fmt.Errorf("capture dependency edges for rename %s -> %s: %w", oldID, newID, err)
		}
	}
	if IsActiveWispInTx(ctx, tx, oldID) {
		if err := updateWispIDInTx(ctx, tx, oldID, newID, issue, actor); err != nil {
			return err
		}
	} else if err := updateIssueIDInTx(ctx, tx, oldID, newID, issue, actor); err != nil {
		return err
	}
	return recordRenameInJournal(ctx, tx, oldID, newID, actor, renameEdges)
}

// recordRenameInJournal replays a rename as the operations a consumer can apply
// without understanding identity changes: drop the old edges, delete the old
// bead, create the new one, re-add the edges under the new id.
func recordRenameInJournal(ctx context.Context, tx DBTX, oldID, newID, actor string, edges []journalDependencyEdge) error {
	for _, edge := range edges {
		if err := RecordDepEventInTx(ctx, tx, EventDepRemove, edge.source, edge.kind, edge.target, edge.metadata, actor); err != nil {

View on GitHub (pinned to 71377f2769)