gastownhall/beads · critical

load %s: %w (refusing to fall back to the embedded store)

Error message

load %s: %w (refusing to fall back to the embedded store)

What it means

openNonMutatingStoreFromConfig treats a present-but-unloadable metadata.json as a hard error, mirroring newDoltStoreFromConfig's contract: no silent fallback to the embedded store. The error names the config path, the underlying cause, and explicitly says the embedded fallback was refused, so callers see the real cause instead of a misleading downstream "database not found".

Source

Thrown at cmd/bd/store_factory.go:261

// refuses because that repo has not been migrated yet — and it must certainly
// not migrate it.
func newPreviewStoreFromConfig(ctx context.Context, beadsDir string) (storage.DoltStorage, error) {
	return openNonMutatingStoreFromConfig(ctx, beadsDir, true)
}

// openNonMutatingStoreFromConfig deliberately does NOT activate the events
// journal: every arm below opens a store that refuses writes (OpenReadOnly,
// OpenForPreviewCommand, ReadOnly server config), so there is no mutation for a
// journal row to accompany. Registered in the construction guard's exemption
// list with that reason.
func openNonMutatingStoreFromConfig(ctx context.Context, beadsDir string, preview bool) (storage.DoltStorage, error) {
	cfg, err := configfile.Load(beadsDir)
	if err != nil {
		// Same contract as newDoltStoreFromConfig: a present-but-unloadable
		// metadata.json is a hard error, not a silent embedded fallback —
		// and the error must name the real cause rather than the downstream
		// "database not found" the embedded open would produce.
		return nil, fmt.Errorf("load %s: %w (refusing to fall back to the embedded store)", configfile.ConfigPath(beadsDir), err)
	}
	if err := validateConfiguredBackend(cfg); err != nil {
		return nil, err
	}
	cfg = normalizeLoadedConfig(cfg)
	if backend, ok := backends.Lookup(cfg.GetBackend()); ok {
		return backend.OpenReadOnly(ctx, beadsDir)
	}
	if cfg != nil && cfg.IsDoltProxiedServerMode() {
		// TODO: this needs to be uow provider
		return nil, fmt.Errorf("proxy server store needs to be uow provider")
		// return newProxiedServerStore(ctx, &dolt.Config{
		// 	BeadsDir:      beadsDir,
		// 	Database:      cfg.GetDoltDatabase(),
		// 	ProxiedServer: true,
		// 	ReadOnly:      true,
		// })
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Validate and repair metadata.json (the path named in the error) — it must be valid JSON.
  2. Restore the file from git or regenerate it via bd init/backend setup.
  3. Fix read permissions for the invoking user.
  4. Use errors.Unwrap on the returned error for the precise parse/I/O cause.

Example fix

python3 -m json.tool .beads/metadata.json   # locate the parse error
git checkout -- .beads/metadata.json        # or repair by hand, then retry
Defensive patterns

Strategy: try-catch

Validate before calling

data, err := os.ReadFile(configfile.ConfigPath(beadsDir))
if err == nil {
    if err := json.Valid(data); !err {
        // metadata.json is malformed; repair before any read-only command
    }
}

Try / catch

store, err := newReadOnlyStoreFromConfig(ctx, beadsDir)
if err != nil {
    // a present-but-unloadable metadata.json is a hard error; no embedded fallback
    return fmt.Errorf("read-only open: %w", err)
}

Prevention

When it happens

Trigger: newReadOnlyStoreFromConfig or newPreviewStoreFromConfig (read-only bd commands) invoked in a workspace where metadata.json exists but configfile.Load fails (malformed JSON, unreadable, parse error).

Common situations: Corrupt or truncated metadata.json after a crash; hand-edited config with invalid JSON; permission changes after repo clone; read-only command run by a user who cannot read the config.

Related errors


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