gastownhall/beads · error
delete issue from %s: %w
Error message
delete issue from %s: %w
What it means
deleteIssueRowInTx wraps the SQL error from the DELETE statement itself, with the routed table name (issues vs the wisp table) interpolated — table names are hardcoded constants via WispTableRouting. This is a driver/SQL-level failure, not a 'row not found' case (that is a separate sentinel error).
Source
Thrown at internal/storage/issueops/delete.go:58
return fmt.Errorf("journal dependency removals for %s: %w", id, err)
}
if err := deleteIssueRowInTx(ctx, tx, id, isWisp); err != nil {
return err
}
if err := RecomputeIsBlockedInTx(ctx, tx, affectedIssues, affectedWisps); err != nil {
return fmt.Errorf("recompute is_blocked after delete for %s: %w", id, err)
}
return nil
}
//nolint:gosec // G201: table names come from WispTableRouting (hardcoded constants)
func deleteIssueRowInTx(ctx context.Context, tx *sql.Tx, id string, isWisp bool) error {
issueTable, _, _, _ := WispTableRouting(isWisp)
result, err := tx.ExecContext(ctx, fmt.Sprintf("DELETE FROM %s WHERE id = ?", issueTable), id)
if err != nil {
return fmt.Errorf("delete issue from %s: %w", issueTable, err)
}
rows, err := result.RowsAffected()
if err != nil {
return fmt.Errorf("get rows affected: %w", err)
}
if rows == 0 {
// Wrap the sentinel so callers can errors.Is(..., storage.ErrNotFound),
// matching GetIssue/UpdateIssue. The storage conformance suite asserts
// this parity across not-found paths.
return fmt.Errorf("%w: issue %s", storage.ErrNotFound, id)
}
// Journal the delete in the same transaction. This worker backs single
// deletes (DeleteIssueInTx) and the per-wisp branch of the bulk delete
// (DeleteResolvedSetInTx); the bulk regular-issue branch journals its own
// ids directly. The rows==0 return above is what keeps this
// actually-deleted-only. The delete plumbing (storage.DeleteIssue and the
// bulk/cascade resolvers) carries no actor, so the row records none.
if err := RecordDeleteInTx(ctx, tx, id, ""); err != nil {View on GitHub (pinned to 71377f2769)
Solutions
- Read the wrapped SQL error for the concrete driver message (table, constraint, connection).
- Run schema migration if the routed table is missing (stale schema after upgrade).
- Check disk space and DB health if the message indicates I/O or corruption.
- Retry in a fresh transaction if the failure was a dropped connection; never reuse a poisoned tx.
Example fix
// before: ignoring schema drift after a version upgrade
err := storage.DeleteIssue(ctx, db, id) // fails: no such table: issues
// after: ensure migrations run before storage use
if err := bd.Migrate(db); err != nil { return err }
err = storage.DeleteIssue(ctx, db, id) Defensive patterns
Strategy: validation
Validate before calling
// pre-flight: table exists and connection healthy
var one int
if err := db.QueryRowContext(ctx, "SELECT 1").Scan(&one); err != nil { return err }
if err := bd.MigrateIfStale(ctx, db); err != nil { return err } Try / catch
if err := storage.DeleteIssue(ctx, db, id); err != nil {
if errors.Is(err, storage.ErrNotFound) { return nil } // not this error; see not-found
return fmt.Errorf("delete failed: %w", err) // surface the wrapped SQL cause
} Prevention
- Run schema migrations after every beads upgrade.
- Monitor disk space on the Dolt data directory.
- Never target read replicas for writes.
- Retry only on fresh transactions after driver.ErrBadConn.
When it happens
Trigger: Any call into DeleteIssue/DeleteIssues/DeleteResolvedSetInTx where the DELETE ... WHERE id = ? statement returns a driver error: bad connection, table missing, constraint violation from a foreign key not cleaned up, read-only replica.
Common situations: Database file corruption or a stale schema where the routed table doesn't exist; deleting an issue whose dependencies rows were created outside the tx by another process with FK enforcement; disk-full on the Dolt data directory.
Related errors
- failed to remove federation peer: %w
- db: ChildCounterSQLRepository.NextChildID: probe parent tabl
- db: ChildCounterSQLRepository.NextChildID: read counter for
- db: ChildCounterSQLRepository.NextChildID: scan existing chi
- db: LabelSQLRepository.Insert %s/%s: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/49fbfc7bdf92096c.
Report an issue: GitHub.