gastownhall/beads · error
db: DependencySQLRepository.Delete: %s -> %s: %w
Error message
db: DependencySQLRepository.Delete: %s -> %s: %w
What it means
This error wraps the driver error from the DELETE statement removing the dependency edge row. The type lookup confirmed the edge existed, but the actual DELETE FROM <table> WHERE issue_id = ? AND <target> = ? failed.
Source
Thrown at internal/storage/domain/db/dependency.go:354
var depType, depMetadata string
//nolint:gosec // G201: table and depTargetExpr are hardcoded constants
err := r.runner.QueryRowContext(ctx,
fmt.Sprintf("SELECT type, metadata FROM %s WHERE issue_id = ? AND %s = ?", table, depTargetExpr),
issueID, dependsOnID,
).Scan(&depType, &depMetadata)
switch {
case errors.Is(err, sql.ErrNoRows):
return domain.DepDeleteResult{Found: false}, nil
case err != nil:
return domain.DepDeleteResult{}, fmt.Errorf("db: DependencySQLRepository.Delete: lookup type %s -> %s: %w", issueID, dependsOnID, err)
}
//nolint:gosec // G201: table and depTargetExpr are hardcoded constants
if _, err := r.runner.ExecContext(ctx,
fmt.Sprintf("DELETE FROM %s WHERE issue_id = ? AND %s = ?", table, depTargetExpr),
issueID, dependsOnID,
); err != nil {
return domain.DepDeleteResult{}, fmt.Errorf("db: DependencySQLRepository.Delete: %s -> %s: %w", issueID, dependsOnID, err)
}
// The type lookup above returned Found:false when no edge existed, so reaching
// here means a row was deleted — record the dependency_removed event on the
// source's event table, matching the embedded/issueops RemoveDependencyInTx path.
// Gated on EmitEvent so only the explicit `bd dep remove` verb emits.
if opts.EmitEvent {
if err := r.events.Record(ctx, domain.Event{
IssueID: issueID,
Type: types.EventDependencyRemoved,
Actor: actor,
NewValue: fmt.Sprintf("Removed dependency on %s", dependsOnID),
}, domain.RecordEventOpts{UseWispsTable: opts.UseWispsTable}); err != nil {
return domain.DepDeleteResult{}, fmt.Errorf("db: DependencySQLRepository.Delete: record dependency_removed event: %w", err)
}
}
dt := types.DependencyType(depType)View on GitHub (pinned to 71377f2769)
Solutions
- Retry after checking for lock contention (another bd process or sync holding the row).
- Verify the database is writable (not a read-only replica).
- Check connectivity and re-run; Delete is safe to retry since it re-looks-up the edge.
- Inspect the wrapped driver error for the specific SQL failure.
Defensive patterns
Strategy: retry
Validate before calling
// confirm edge exists and DB is writable before Delete
var depType string
if err := db.QueryRow("SELECT type FROM dependencies WHERE issue_id=? AND depends_on_id=?", issueID, dependsOnID).Scan(&depType); err != nil {
return err
} Try / catch
res, err := repo.Delete(ctx, issueID, dependsOnID)
if err != nil {
if strings.Contains(err.Error(), "Delete:") {
// DELETE failed: check locks/read-only state, retry safely (lookup re-runs)
}
}
if res != nil && !res.Found { /* edge absent */ } Prevention
- Avoid concurrent writers on the same edge; serialize deletes.
- Point bd at a writable primary.
- Retry deletes on transient failure — they re-verify existence first.
When it happens
Trigger: Calling Delete on an existing edge where ExecContext fails — foreign-key restriction from child rows, connection loss, read-only backend, or lock contention.
Common situations: Row locked by a concurrent transaction; database on a read-only replica; driver-level FK restrictions; storage quota/connection issues.
Related errors
- db: DependencySQLRepository.Delete: lookup type %s -> %s: %w
- failed to begin transaction: %w
- failed to recompute is_blocked: %w
- failed to commit is_blocked repairs: %w
- %s: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/fa6d6ac983172591.
Report an issue: GitHub.