gastownhall/beads · error

delete issues: %w

Error message

delete issues: %w

What it means

DeleteResolvedSetInTx fails with this when the batched `DELETE FROM issues WHERE id IN (...)` statement errors. Regular (non-wisp) issues are removed in batches of deleteBatchSize inside one transaction; any SQL-level failure aborts the whole delete.

Source

Thrown at internal/storage/issueops/delete.go:321

		if err := deleteIssueRowInTx(ctx, tx, id, true); err != nil {
			return nil, fmt.Errorf("delete wisp %s: %w", id, err)
		}
	}

	totalRegularsDeleted := 0
	for i := 0; i < len(set.RegularIDs); i += deleteBatchSize {
		end := i + deleteBatchSize
		if end > len(set.RegularIDs) {
			end = len(set.RegularIDs)
		}
		batch := set.RegularIDs[i:end]
		batchInClause, batchArgs := buildSQLInClause(batch)

		deleteResult, err := tx.ExecContext(ctx,
			fmt.Sprintf(`DELETE FROM issues WHERE id IN (%s)`, batchInClause),
			batchArgs...)
		if err != nil {
			return nil, fmt.Errorf("delete issues: %w", err)
		}
		rowsAffected, _ := deleteResult.RowsAffected()
		totalRegularsDeleted += int(rowsAffected)

		// Deleted issues hold no leases.
		if _, err := tx.ExecContext(ctx,
			fmt.Sprintf(`DELETE FROM leases WHERE issue_id IN (%s)`, batchInClause),
			batchArgs...); err != nil {
			return nil, fmt.Errorf("delete leases: %w", err)
		}
	}
	result.DeletedCount = totalRegularsDeleted + len(set.WispIDs)

	// Journal every regular issue this bulk/cascade delete removed. Wisps went
	// through deleteIssueRowInTx above, which journals each itself; set.All is
	// cascade-expanded, so this records cascade deletes too. The delete
	// plumbing carries no actor, so the rows record none.
	for _, id := range journaledDeletes {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check the wrapped driver error (lock timeout, too many parameters, connection reset)
  2. Reduce delete set size or retry when the DB is not contended
  3. For SQLite busy errors, raise the busy timeout / close other writers
  4. Ensure the schema is migrated before deleting

Example fix

// before
result, err := tx.ExecContext(ctx, fmt.Sprintf(`DELETE FROM issues WHERE id IN (%s)`, batchInClause), batchArgs...)
if err != nil { return nil, fmt.Errorf("delete issues: %w", err) }
// after — same error path; avoid busy-lock by configuring the driver
// e.g. dsn := "file:beads.db?_busy_timeout=5000"
Defensive patterns

Strategy: try-catch

Validate before calling

// confirm target IDs exist and the DB is writable before the bulk delete
if err := db.PingContext(ctx); err != nil { return err }
// optionally: verify no other process holds a write lock (SQLite)

Try / catch

if _, err := DeleteIssuesInTx(ctx, tx, ids, opts); err != nil {
	if strings.Contains(err.Error(), "delete issues: ") {
		log.Printf("batch DELETE failed: %v", err) // inspect wrapped driver error
	}
	return err
}

Prevention

When it happens

Trigger: DeleteIssuesInTx/DeleteInTx executing a cascade or bulk delete where the DELETE statement fails — too many parameters for the driver, table lock/timeout, or connection loss during tx.ExecContext.

Common situations: Very large batch deletes exceeding driver parameter limits on some engines; database locked by another writer (SQLite busy); network interruption to a remote Dolt server.

Related errors


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