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

  1. Read the wrapped driver error: lock wait timeout => retry when the other writer finishes; FK violation => remove or update the referencing rows first
  2. Retry the rename — the transaction rolls back the read rows so nothing is lost
  3. Run renames serially; avoid two bd processes renaming overlapping issues
  4. 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

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


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