gastownhall/beads · error
retarget to %s collides with existing dependency target in %
Error message
retarget to %s collides with existing dependency target in %s
What it means
This is a deliberate domain error: while retargeting inbound dependencies for id, checkRetargetTargetCollision found that the same (issue_id, target) edge would exist in both its current form and the retargeted form — i.e. an issue already depends on id via the destination column (issue, wisp, or external target). Performing the UPDATE would create a duplicate/conflicting edge, so the operation is aborted with this message instead.
Source
Thrown at internal/storage/issueops/dependencies.go:866
SELECT 1 FROM %s moving
JOIN %s existing ON moving.issue_id = existing.issue_id
WHERE moving.%s = ?
AND (existing.%s = ? OR existing.%s = ?)
LIMIT 1
`, table, table, sourceCol, conflictCols[0], conflictCols[1])
var found int
err := tx.QueryRowContext(ctx, query, id, id, id).Scan(&found)
if err == sql.ErrNoRows {
return nil
}
if err != nil {
if isTableNotExistError(err) {
return nil
}
return fmt.Errorf("check retarget collision in %s: %w", table, err)
}
return fmt.Errorf("retarget to %s collides with existing dependency target in %s", id, table)
}
//nolint:gosec // G201: table and typedCol are hardcoded constants.
func checkRenameTargetCollision(ctx context.Context, tx DBTX, table, typedCol, newID string) error {
var otherCols []string
switch typedCol {
case "depends_on_issue_id":
otherCols = []string{"depends_on_wisp_id", "depends_on_external"}
case "depends_on_wisp_id":
otherCols = []string{"depends_on_issue_id", "depends_on_external"}
default:
return fmt.Errorf("checkRenameTargetCollision: unsupported typed column %q", typedCol)
}
query := fmt.Sprintf(`
SELECT 1 FROM %s a
JOIN %s b ON a.issue_id = b.issue_id
WHERE a.%s = ?View on GitHub (pinned to 71377f2769)
Solutions
- Find the conflicting rows: SELECT issue_id FROM <table> WHERE <sourceCol>='id' AND (<destCol>='id' OR depends_on_external='id').
- Delete or merge the redundant duplicate edge so only one representation remains, then retry the move.
- Run bd doctor / consistency checks to detect and repair duplicated dependency edges.
- If caused by a prior partial migration, repair the affected rows before promoting/moving the issue.
Example fix
// before: move blocked by duplicate edge // bd-7 depends_on bd-42 (issue) AND bd-42 (wisp) // after: drop the stale duplicate edge first DELETE FROM dependencies WHERE issue_id='bd-7' AND depends_on_wisp_id='bd-42'; err := MoveIssuePersistenceInTx(ctx, tx, "bd-42")
Defensive patterns
Strategy: validation
Validate before calling
func retargetCollides(ctx context.Context, db *sql.DB, table, id string) (bool, error) {
const q = `SELECT 1 FROM ` + table + ` moving JOIN ` + table +
` existing ON moving.issue_id = existing.issue_id
WHERE moving.depends_on_issue_id = ?
AND (existing.depends_on_wisp_id = ? OR existing.depends_on_external = ?) LIMIT 1`
var one int
err := db.QueryRowContext(ctx, q, id, id, id).Scan(&one)
if errors.Is(err, sql.ErrNoRows) { return false, nil }
return err == nil, err
}
// call before moving; if true, clean the duplicate edge first Type guard
func isRetargetCollision(err error) bool {
return err != nil && strings.Contains(err.Error(), "collides with existing dependency target") && strings.Contains(err.Error(), "retarget to")
} Try / catch
if err := store.MoveIssue(ctx, id); isRetargetCollision(err) {
// surface the conflicting issue_id from the message and ask the user to dedupe
return fmt.Errorf("resolve duplicate dependency edges before moving %s: %w", id, err)
} Prevention
- Deduplicate dependency edges after imports or syncs from external tools.
- Never hand-edit dependency rows; use bd dep add/remove so invariants hold.
- Run periodic consistency checks (bd doctor) to catch shadow wisp/issue edges.
- After any failed/interrupted move, verify no dual-representation edges were left behind.
When it happens
Trigger: RetargetInboundDependenciesToWispInTx or RetargetInboundDependenciesToIssueInTx is invoked (via MoveIssuePersistenceInTx / PromoteFromEphemeralInTx) and a row exists where the same issue_id has BOTH the source-column target = id and a destination-column target (depends_on_issue_id / depends_on_wisp_id / depends_on_external) = id.
Common situations: An issue has parallel edges recorded against both representations of the same work item (e.g. after a partial earlier move or manual row edits); duplicate imports created shadow edges; data drift between dependencies and wisp_dependencies from an older buggy version.
Related errors
- delete: drop sync-plane edges into deleted wisps: %w
- rename to %s collides with existing dependency target in %s
- no store is open for this workspace
- load dependency records: %w
- failed to load dependencies: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/52ae76532d2a9f6a.
Report an issue: GitHub.