gastownhall/beads · error

rekey dependency source id %s -> %s: %w

Error message

rekey dependency source id %s -> %s: %w

What it means

This error wraps failure of the per-row UPDATE that rewrites a stale dependency primary key: `UPDATE dependencies SET id = ?, issue_id = ? WHERE id = ?` in rekeyDependencySourceInTx. The most common cause is a primary/unique key conflict — the desired new id depid.New(newID, target) already exists on another row (a duplicate edge).

Source

Thrown at internal/storage/issueops/dependencies.go:670

			return fmt.Errorf("scan dependency source: %w", err)
		}
		target, ok := resolveDependencyTarget(issueTarget, wispTarget, external)
		if !ok {
			continue // ck_dep_one_target guarantees one target; skip defensively
		}
		if want := depid.New(newID, target); want != id {
			rekeys = append(rekeys, rekey{oldRowID: id, newRowID: want})
		}
	}
	_ = queryRows.Close()
	if err := queryRows.Err(); err != nil {
		return fmt.Errorf("iterate dependency sources: %w", err)
	}
	for _, rk := range rekeys {
		if _, err := tx.ExecContext(ctx,
			"UPDATE dependencies SET id = ?, issue_id = ? WHERE id = ?",
			rk.newRowID, newID, rk.oldRowID); err != nil {
			return fmt.Errorf("rekey dependency source id %s -> %s: %w", rk.oldRowID, rk.newRowID, err)
		}
	}
	return nil
}

// resolveDependencyTarget returns the single non-null dependency target — the value
// depid.New and the uk_dep_* unique keys treat as the edge's target — following the
// same precedence as DepTargetExpr (issue, then wisp, then external).
func resolveDependencyTarget(issueTarget, wispTarget, external sql.NullString) (string, bool) {
	switch {
	case issueTarget.Valid:
		return issueTarget.String, true
	case wispTarget.Valid:
		return wispTarget.String, true
	case external.Valid:
		return external.String, true
	default:
		return "", false

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped driver error; `Duplicate entry ... for key PRIMARY` confirms an id collision
  2. Find the colliding row: SELECT id, issue_id, depends_on_issue_id, depends_on_wisp_id, depends_on_external FROM dependencies WHERE id = depid.New(newID, target)
  3. Remove the duplicate edge before renaming (it will be re-created by the rekey)
  4. Retry inside one transaction — partial rekeys roll back, so no manual cleanup of half-updated ids should be needed

Example fix

// before: rename collides with existing edge id
UPDATE dependencies SET id='bd-100->bd-200', issue_id='bd-100' WHERE id='bd-101->bd-200';
-- Error 1062: Duplicate entry 'bd-100->bd-200' for key 'PRIMARY'
// after: deduplicate first
DELETE FROM dependencies WHERE id='bd-100->bd-200';  -- then retry the rename
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check that no existing edge already carries the post-rename deterministic id
for _, target := range sourceTargets(oldID) {
    want := depid.New(newID, target)
    var exists bool
    db.Get(&exists, `SELECT COUNT(*) > 0 FROM dependencies WHERE id = ? AND issue_id <> ?`, want, oldID)
    if exists {
        return fmt.Errorf("edge %s already exists; remove the duplicate before renaming %s to %s", want, oldID, newID)
    }
}

Try / catch

err := updateIssueID(tx, oldID, newID)
if err != nil {
    var dup *mysql.MySQLError
    if errors.As(err, &dup) && dup.Number == 1062 {
        return fmt.Errorf("remove duplicate dependency edge %q before renaming: %w", extractKey(dup), err)
    }
    return err
}

Prevention

When it happens

Trigger: UpdateIssueIDInTx rename where, for some edge, depid.New(newID, target) collides with the id of an existing dependency row — e.g. both bd-101 and bd-100 already have an edge to the same target, or a prior partial rekey left overlapping rows.

Common situations: Renaming an issue to an ID that already has edges to the same targets; clone/merge divergence (#4259 scenario) leaving both stale and fresh key variants of the same edge; concurrent renames racing on the same rows.

Related errors


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