gastownhall/beads · error

storage backend has no underlying database

Error message

storage backend has no underlying database

What it means

openStoreDB opens the beads storage store and fetches its underlying *sql.DB via store.UnderlyingDB(). Some backends have no SQL database underneath; if the handle is nil the store is closed and this error returned, since the SQL-level checks cannot run.

Source

Thrown at cmd/bd/doctor/validation.go:30

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

// openStoreDB opens the beads database and returns the underlying *sql.DB for
// raw queries. The caller must close the returned store when done.
func openStoreDB(beadsDir string) (*sql.DB, storage.DoltStorage, error) {
	ctx := context.Background()
	doltPath := getDatabasePath(beadsDir)
	cfg := doltServerConfig(beadsDir, doltPath)
	store, err := dolt.New(ctx, cfg)
	if err != nil {
		return nil, nil, err
	}
	db := store.UnderlyingDB()
	if db == nil {
		_ = store.Close() // Best effort cleanup
		return nil, nil, fmt.Errorf("storage backend has no underlying database")
	}
	return db, store, nil
}

// CheckOrphanedDependencies detects dependencies pointing to non-existent issues.
func CheckOrphanedDependencies(path string) DoctorCheck {
	beadsDir := ResolveBeadsDirForRepo(path)

	db, store, err := openStoreDB(beadsDir)
	if err != nil {
		return DoctorCheck{
			Name:    "Orphaned Dependencies",
			Status:  "ok",
			Message: "N/A (no database)",
		}
	}
	defer func() { _ = store.Close() }()

View on GitHub (pinned to 71377f2769)

Solutions

  1. Switch the repository to a Dolt backend so an underlying SQL database exists
  2. Skip database-level checks for non-SQL backends and use file-based validations instead
  3. Check store construction/initialization if a Dolt backend unexpectedly returns nil

Example fix

// before
DoctorCheck := CheckDuplicateIssues(path) // works only for SQL backends
// after
if store.UnderlyingDB() != nil {
    DoctorCheck = CheckDuplicateIssues(path)
} else { /* file-based check */ }
Defensive patterns

Strategy: type-guard

Validate before calling

store, err := openStore(path)
if err == nil && store.UnderlyingDB() == nil {
    // non-SQL backend: use file-based checks instead
}

Type guard

func hasUnderlyingDB(s storage.Store) bool { return s != nil && s.UnderlyingDB() != nil }

Try / catch

if err != nil && strings.Contains(err.Error(), "no underlying database") {
    // fall back to JSONL/file-based validation path
}

Prevention

When it happens

Trigger: Calling openStoreDB (directly or via CheckStaleClosedIssues, CheckOrphanedDependencies, CheckDuplicateIssues, CheckTestPollution, CheckChildParentDependencies, wisp misclassification checks) on a backend whose UnderlyingDB() returns nil — e.g. JSONL/file backend.

Common situations: Repo configured for the JSONL (non-Dolt) backend while running database-backed doctor validations; embedded/mock store in tests lacking a DB; storage factory returning a wrapper without DB support.

Related errors


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