gastownhall/beads · error

delete: resolve ids: %w

Error message

delete: resolve ids: %w

What it means

This error wraps a failure from GetIssuesByIDsInTx while DeleteInTx (the store-backed body of `bd delete`) resolves the requested ids to rows inside the delete transaction. It runs after the wisp-plane classification, so any SQL-level or driver-level failure during the batched id lookup surfaces here with the 'delete: resolve ids' prefix. It is an infrastructure wrapper, not a domain refusal — absent ids surface separately as a NotFoundError.

Source

Thrown at internal/storage/issueops/delete_role.go:42

// BEFORE opening a transaction, so a malformed request costs no database work.
//
// THE REWRITE IS INSIDE THE TRANSACTION. A route that deleted the rows in one
// transaction and rewrote the neighbors' text afterwards left, on a failure
// between the two, a workspace whose rows were gone and whose descriptions
// still cited them.
func DeleteInTx(ctx context.Context, tx *sql.Tx, req publicops.DeleteRequest) (publicops.DeleteResult, error) {
	ids := req.IDs
	result := publicops.DeleteResult{DryRun: req.DryRun}

	// The existence probe comes FIRST, so `bd delete typo real` reports the
	// typo rather than whatever the graph says about the id that resolved.
	wispSet, err := WispIDSetInTx(ctx, tx, ids)
	if err != nil {
		return publicops.DeleteResult{}, fmt.Errorf("delete: classify planes: %w", err)
	}
	found, err := GetIssuesByIDsInTx(ctx, tx, ids, wispSet)
	if err != nil {
		return publicops.DeleteResult{}, fmt.Errorf("delete: resolve ids: %w", err)
	}
	present := make(map[string]bool, len(found))
	for _, issue := range found {
		if issue != nil {
			present[issue.ID] = true
		}
	}
	var missing []string
	for _, id := range ids {
		if !present[id] {
			missing = append(missing, id)
		}
	}
	if len(missing) > 0 {
		return publicops.DeleteResult{}, &publicops.NotFoundError{IDs: missing}
	}

	// The version precondition sits between the existence probe and the

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped error (%w / errors.Unwrap) for the real driver message
  2. Check database connectivity and that the transaction is still alive (no prior error/rollback)
  3. Verify the storage schema is current (run migrations / `bd doctor`)
  4. Retry the delete with a fresh transaction if the context was cancelled or the connection dropped
  5. Reduce batch size or split large id lists if timeouts are the cause

Example fix

// before: unbounded ctx
res, err := store.Delete(ctx, req)
// after: bound the operation and handle context expiry
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
res, err := store.Delete(ctx, req)
if err != nil && strings.Contains(err.Error(), "delete: resolve ids") { /* check DB health, retry */ }
Defensive patterns

Strategy: try-catch

Validate before calling

ids := normalizeDeleteIDs(req.IDs)
if len(ids) == 0 { return errors.New("no ids to delete") }
if err := ctx.Err(); err != nil { return err } // ctx must be live before opening the tx

Type guard

var serr *publicops.NotFoundError
if errors.As(err, &serr) { /* domain refusal, not an infra failure */ }
// otherwise the 'delete: resolve ids' wrapper indicates a storage failure

Try / catch

res, err := store.Delete(ctx, req)
if err != nil {
    if strings.Contains(err.Error(), "delete: resolve ids") {
        // storage-layer failure; check DB health, retry with fresh tx
    }
    return err
}

Prevention

When it happens

Trigger: DeleteInTx (or DeleteIssuesInTx via issueops.Deleter) calls GetIssuesByIDsInTx over the requested ids and the underlying query/scan/rows iteration fails — driver error, context cancellation mid-query, or a broken connection inside the open transaction.

Common situations: Database connection dropped or transaction invalidated before the probe; context deadline exceeded during a large --from-file batch; backend schema drift breaking the issues lookup; partially migrated database.

Related errors


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