gastownhall/beads · error
delete config %s: %w
Error message
delete config %s: %w
What it means
DeleteConfigInTx wraps the ExecContext error from deleting a row in the config table with this message, including the key being deleted. It means the DELETE statement itself failed — not that the key was missing (a missing key deletes zero rows without error).
Source
Thrown at internal/storage/issueops/bulk_ops.go:96
for wispRows.Next() {
var id string
if err := wispRows.Scan(&id); err != nil {
return nil, fmt.Errorf("scan wisp issue id: %w", err)
}
ids = append(ids, id)
}
if err := wispRows.Err(); err != nil {
return nil, fmt.Errorf("iterate wisp issues by label: %w", err)
}
return ids, nil
}
// DeleteConfigInTx removes a configuration value.
func DeleteConfigInTx(ctx context.Context, tx *sql.Tx, key string) error {
_, err := tx.ExecContext(ctx, "DELETE FROM config WHERE `key` = ?", key)
if err != nil {
return fmt.Errorf("delete config %s: %w", key, err)
}
return nil
}
// GetCommentsForIssuesInTx retrieves comments for multiple issues, partitioning
// between comments and wisp_comments tables.
//
//nolint:gosec // G201: table is hardcoded
func GetCommentsForIssuesInTx(ctx context.Context, tx *sql.Tx, issueIDs []string) (map[string][]*types.Comment, error) {
if len(issueIDs) == 0 {
return make(map[string][]*types.Comment), nil
}
result := make(map[string][]*types.Comment)
// Partition IDs by wisp status in a single batched query, to avoid N
// round-trips on remote backends (GH#3414).
wispIDs, permIDs, err := PartitionWispIDsInTx(ctx, tx, issueIDs)View on GitHub (pinned to 71377f2769)
Solutions
- Inspect the wrapped driver error; fix the underlying cause (connectivity, schema, locking) and retry the transaction.
- Extend or fix the context deadline if the delete was cancelled by timeout.
- Verify the config table exists and matches the expected schema (run bd doctor / migrations) if the error indicates a SQL/schema problem.
Example fix
// before
err := issueops.DeleteConfigInTx(ctx, tx, "my.key")
// after
if err := issueops.DeleteConfigInTx(ctx, tx, "my.key"); err != nil {
if errors.Is(ctx.Err(), context.DeadlineExceeded) {
ctx, cancel = context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
err = retryTx(ctx, func(tx *sql.Tx) error { return issueops.DeleteConfigInTx(ctx, tx, "my.key") })
}
} Defensive patterns
Strategy: try-catch
Validate before calling
func canDeleteConfig(ctx context.Context, tx *sql.Tx) error {
if ctx.Err() != nil { return ctx.Err() }
var n int
return tx.QueryRowContext(ctx, "SELECT COUNT(*) FROM config").Scan(&n)
} Try / catch
if err := issueops.DeleteConfigInTx(ctx, tx, key); err != nil {
var base error
errors.As(err, &base) // inspect wrapped driver error
return fmt.Errorf("config delete aborted, retry whole tx: %w", err)
} Prevention
- Wrap deletes in a transaction you can retry whole on transient failures.
- Check ctx.Err() before issuing writes with tight deadlines.
- Verify config table schema exists before delete operations (migrations/doctor).
- Distinguish 'no rows deleted' (success) from exec errors — don't pre-check existence.
When it happens
Trigger: tx.ExecContext("DELETE FROM config WHERE `key` = ?") fails: connection loss, context cancellation, SQL syntax/schema errors, table locked, or read-only transaction.
Common situations: Dolt server unavailable mid-transaction; schema drift where the config table is missing; context deadline expiring during the delete; embedded database file locked by another process.
Related errors
- failed to begin transaction: %w
- failed to recompute is_blocked: %w
- failed to commit is_blocked repairs: %w
- failed to query orphaned dependencies: %w
- row iteration error: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/c9ed68059f482fb4.
Report an issue: GitHub.