gastownhall/beads · error

check %s for issue %q: %w

Error message

check %s for issue %q: %w

What it means

EnsureIssueIDAvailableInTx failed while counting rows for the ID in the issues or wisps tables. This is the existence-probe step of the guard; a scan/query failure means the storage backend errored, not that the ID is taken (a taken ID yields the distinct ErrAlreadyExists wrap at index 3466).

Source

Thrown at internal/storage/issueops/create_only_guard.go:27

	"github.com/steveyegge/beads/internal/storage"
)

// EnsureIssueIDAvailableInTx serializes same-shard creates and rejects occupied IDs.
func EnsureIssueIDAvailableInTx(ctx context.Context, tx DBTX, id string) error {
	if tx == nil {
		return fmt.Errorf("ensure issue ID available: transaction is nil")
	}
	if id == "" {
		return fmt.Errorf("ensure issue ID available: ID is empty")
	}
	key := issueCreateCoordinationKey(id)
	if _, err := tx.ExecContext(ctx, "REPLACE INTO local_metadata (`key`, value) VALUES (?, ?)", key, strconv.FormatInt(FreshRowLock(), 10)); err != nil {
		return fmt.Errorf("coordinate issue create: %w", err)
	}
	for _, table := range []string{"issues", "wisps"} {
		var count int
		if err := tx.QueryRowContext(ctx, "SELECT COUNT(*) FROM "+table+" WHERE id = ?", id).Scan(&count); err != nil {
			return fmt.Errorf("check %s for issue %q: %w", table, id, err)
		}
		if count > 0 {
			return fmt.Errorf("%w: %s", storage.ErrAlreadyExists, id)
		}
	}
	return nil
}

func issueCreateCoordinationKey(id string) string {
	sum := sha256.Sum256([]byte(id))
	shard := uint16(sum[0])<<4 | uint16(sum[1])>>4
	return fmt.Sprintf("issue-create/v1/%03x", shard)
}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped error to identify which table and what driver error occurred
  2. Verify the schema is current (apply pending migrations)
  3. Retry with a fresh transaction and healthy connection

Example fix

// before
// assume table exists
// after
// run migrations first, then retry create in a new tx
Defensive patterns

Strategy: retry

Validate before calling

// ensure schema is migrated before issuing creates
// SELECT COUNT(*) FROM issues LIMIT 1; SELECT COUNT(*) FROM wisps LIMIT 1;

Try / catch

if err := CreateIssueInTxWithResult(ctx, tx, issue); err != nil {
    if strings.HasPrefix(err.Error(), "check ") {
        cause := errors.Unwrap(err)
        // migrate/reconnect, then retry in a new tx
    }
    return err
}

Prevention

When it happens

Trigger: QueryRowContext("SELECT COUNT(*) FROM issues|wisps WHERE id = ?") returns a driver error — corrupted schema, missing table, connection drop mid-transaction.

Common situations: Migrations not applied (table absent); Dolt/driver-level failures during heavy concurrent imports; DB connectivity loss.

Related errors


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