gastownhall/beads · error
errClosed
errClosed
Error message
embeddeddolt: store is closed
What it means
errClosed is returned by any store method invoked after Close(): the connection helpers (withConn, withDBConn, withPinnedDBConn) and ApplySchemaMigrations check the closed flag first. IsClosed() exposes the same state so callers like maybeAutoCommit can skip work on a closed store without erroring.
Source
Thrown at internal/storage/embeddeddolt/store.go:91
const (
// openStrict is the default: any pending-migration refusal fails the
// open. Used by Open.
openStrict openIntent = iota
// openReadOnlyCommand relaxes both refusals for read-only commands: they
// must keep working on the current schema until the operator makes the
// migrate-or-adopt decision (bd-578h9.5), and must not be bricked by
// dirty tables either. Used by OpenForReadOnlyCommand.
openReadOnlyCommand
// openWorkingSetReconcile relaxes both refusals for working-set-reconcile
// commands (bd dolt commit, bd vc commit): their entire purpose is to
// clear the dirty working set that a migration would otherwise refuse to
// touch, so failing the open here would deadlock the documented recovery
// (#4566). Used by OpenForWorkingSetReconcile.
openWorkingSetReconcile
)
// errClosed is returned when a method is called after Close.
var errClosed = errors.New("embeddeddolt: store is closed")
// IsClosed reports whether the store has been closed. Implements
// storage.LifecycleManager so that callers (e.g., maybeAutoCommit) can
// skip operations on a closed store without triggering errClosed.
func (s *EmbeddedDoltStore) IsClosed() bool {
return s.closed.Load()
}
// newStore creates an EmbeddedDoltStore using the embedded Dolt engine.
// beadsDir is the .beads/ root; the data directory is derived as <beadsDir>/embeddeddolt/.
// The database is created automatically if it doesn't exist (initSchema handles this).
//
// The dolthub/driver/v2 handles its own concurrency internally. File-level locking
// is only used during bd init (via util.TryLock in the init command) to protect
// one-time initialization steps — the store itself does not hold any lock.
func newStore(ctx context.Context, beadsDir, database, branch string, intent openIntent) (*EmbeddedDoltStore, error) {
if database == "" {
return nil, fmt.Errorf("embeddeddolt: database name must not be empty (caller should default to %q)", "beads")View on GitHub (pinned to 71377f2769)
Solutions
- Check store.IsClosed() (or errors.Is(err, errClosed) via the exported detection) before operations in background/deferred paths
- Restructure so the store outlives all users — close only after goroutines have joined
- Use sync.WaitGroup/context cancellation so pending operations complete or abort before Close
Example fix
// before
store.Close()
go store.Flush(ctx) // returns errClosed
// after
err := store.Flush(ctx)
store.Close()
// or guard:
if !store.IsClosed() { _ = store.Flush(ctx) } Defensive patterns
Strategy: type-guard
Validate before calling
if store.IsClosed() {
return nil // skip background flush/commit
} Type guard
func isOpen(store *embeddeddolt.EmbeddedDoltStore) bool { return !store.IsClosed() } Try / catch
if err := store op(ctx); err != nil {
if strings.Contains(err.Error(), "store is closed") { // prefer IsClosed() pre-check
return nil
}
return err
} Prevention
- Check IsClosed() in all background/deferred paths before touching the store
- Close the store only after all goroutines have completed (WaitGroup)
- Keep store lifetime scoped to the command; never cache handles past Close
When it happens
Trigger: Calling any store operation after EmbeddedDoltStore.Close() has returned — e.g. background goroutines, deferred writes, or auto-commit paths racing with shutdown.
Common situations: Deferred/async work (flush, auto-commit, tip-metadata write) firing after a command finished and closed the store; using a store handle beyond its lifecycle scope.
Related errors
- errIdleTimeout
- server: ExternalDoltServer.Start: server already started
- invalid database name: %q; hyphens are not allowed in embedd
- embeddeddolt: requires CGO (build with CGO_ENABLED=1)
- ErrReadOnly
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/ca16acfc846283e7.
Report an issue: GitHub.