gastownhall/beads · error
delete old dependency target: %w
Error message
delete old dependency target: %w
What it means
This error wraps failure of the DELETE that removes old-target rows in replaceDependencyTargetInTx, just before reinserting them with the new target. It means the `DELETE FROM <table> WHERE <column> = ? OR (<DepTargetExpr> = ? AND depends_on_external IS NULL)` statement failed at the driver/schema level (not a duplicate-key problem — this is a delete).
Source
Thrown at internal/storage/issueops/dependencies.go:750
row.external = sql.NullString{}
case "depends_on_wisp_id":
row.issueTarget = sql.NullString{}
row.wispTarget = sql.NullString{String: newID, Valid: true}
row.external = sql.NullString{}
default:
_ = queryRows.Close()
return fmt.Errorf("replace dependency target: unsupported typed column %q", column)
}
rows = append(rows, row)
}
_ = queryRows.Close()
if err := queryRows.Err(); err != nil {
return fmt.Errorf("iterate dependency targets: %w", err)
}
//nolint:gosec // table and column are hardcoded by callers.
if _, err := tx.ExecContext(ctx, fmt.Sprintf(`DELETE FROM %s WHERE %s = ? OR (%s = ? AND depends_on_external IS NULL)`, table, column, DepTargetExpr), oldID, oldID); err != nil {
return fmt.Errorf("delete old dependency target: %w", err)
}
for _, row := range rows {
// The retargeted edge's natural key is (issue_id, newID): the switch above
// set exactly one typed target column to newID. Re-derive id from it so the
// rewritten row stays merge-safe and keeps a clone-stable primary key (#4259).
//nolint:gosec // table is hardcoded by callers.
if _, err := tx.ExecContext(ctx, fmt.Sprintf(`
INSERT INTO %s (id, issue_id, depends_on_issue_id, depends_on_wisp_id, depends_on_external, type, created_at, created_by, metadata, thread_id)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, table), depid.New(row.issueID, newID), row.issueID, nullStringValue(row.issueTarget), nullStringValue(row.wispTarget), nullStringValue(row.external), row.depType, nullTimeValue(row.createdAt), nullStringValue(row.createdBy), nullStringValue(row.metadata), nullStringValue(row.threadID)); err != nil {
return fmt.Errorf("insert replacement dependency target: %w", err)
}
}
return nil
}
func nullStringValue(value sql.NullString) any {
if !value.Valid {View on GitHub (pinned to 71377f2769)
Solutions
- Read the wrapped driver error: lock wait timeout => retry when the other writer finishes; FK violation => remove or update the referencing rows first
- Retry the rename — the transaction rolls back the read rows so nothing is lost
- Run renames serially; avoid two bd processes renaming overlapping issues
- Check for long-running transactions holding locks on dependencies rows
Example fix
// before: concurrent writer blocks the delete DELETE FROM dependencies WHERE depends_on_issue_id = 'bd-100' ... -- Error 1205: Lock wait timeout exceeded // after: ensure no other bd process is mid-operation, then retry bd doctor # verify no stale locks/transactions bd update bd-101 --id bd-100
Defensive patterns
Strategy: retry
Validate before calling
// Detect blocking locks or dependent FK rows before the rename
var locks int
db.Get(&locks, `SELECT COUNT(*) FROM information_schema.INNODB_TRX WHERE TRX_STARTED < NOW() - INTERVAL 30 SECOND`)
if locks > 0 { return fmt.Errorf("%d long-running transactions may block the delete; retry later", locks) } Try / catch
err := updateIssueID(tx, oldID, newID)
if err != nil {
var my *mysql.MySQLError
if errors.As(err, &my) && (my.Number == 1205 || my.Number == 1213) {
// lock wait timeout / deadlock: wait and retry the whole rename
}
return err
} Prevention
- Run only one bd mutating process per database at a time
- Keep transactions short so they don't hold dependency-row locks
- Set a sane innodb_lock_wait_timeout and retry whole renames on timeout
- Check FK references before deleting/renaming heavily-linked issues
When it happens
Trigger: UpdateIssueIDInTx / UpdateWispIDInTx rename, when the DELETE on dependencies or wisp_dependencies fails: FK restriction from a child table, aborted transaction, dropped connection, or lock wait timeout with concurrent writers.
Common situations: Concurrent bd processes holding row locks on the same dependency rows; FK constraints from other tables referencing the old rows; connection loss during a bulk rename.
Related errors
- delete wisp %s: %w
- delete issues: %w
- delete leases: %w
- recompute is_blocked after batch delete: %w
- delete wisp %s from dependencies: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/b50037ec2129351c.
Report an issue: GitHub.