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 "", falseView on GitHub (pinned to 71377f2769)
Solutions
- Read the wrapped driver error; `Duplicate entry ... for key PRIMARY` confirms an id collision
- 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)
- Remove the duplicate edge before renaming (it will be re-created by the rekey)
- 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
- Deduplicate edges to shared targets before renaming issues
- Never manually edit dependencies.id; always let beads derive it via depid.New
- Run one rename at a time; concurrent renames can mint identical keys
- After merges/clones, run bd doctor to catch stale vs fresh key variants
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
- insert replacement dependency target: %w
- rekey dependency sources %s -> %s: %w
- ErrTransaction
- open unit of work: %w
- failed to begin transaction: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/682b159c37a1b23c.
Report an issue: GitHub.