gastownhall/beads · critical

load %s: %w

Error message

load %s: %w

What it means

serveDatabaseSource refuses to continue when metadata.json (via configfile.Load) cannot be read or parsed. Per the comment, this is a hard error: the classification's default is an embedded refusal, so falling back would silently refuse a workspace whose backend nobody could read. The error names the actual config path plus the underlying cause.

Source

Thrown at cmd/bd/serve.go:561

// be handed to the Dolt provider and fail with a misleading Dolt error — or
// connect to a defaulted host and serve the wrong database.
//
// EMBEDDED DOLT IS PERMANENT, and this is the only place that refusal lives.
// Its commit protocol runs outside the SQL transaction on a separate
// connection, so the per-request atomicity this server's contract states would
// be a lie there. That is a property of the backend rather than of what has
// been built so far, which is also why no unit-of-work provider for it exists
// or will. Nothing downstream will catch a bypass: internal/httpapi cannot see
// the backend behind a role, and every store publishes every role accessor
// whatever it is. TestServeNamesOneDatabaseSourcePerServerItBuilds pins that
// the roles are only ever reached through here.
func serveDatabaseSource(beadsDir string) (serveDatabase, error) {
	cfg, err := configfile.Load(beadsDir)
	if err != nil {
		// Never classify past an unreadable metadata.json. The classification's
		// default is the embedded refusal, so falling back would refuse a
		// workspace whose real backend nobody managed to read.
		return serveDatabase{}, fmt.Errorf("load %s: %w", configfile.ConfigPath(beadsDir), err)
	}
	if backend := normalizeLoadedConfig(cfg).GetBackend(); backends.Registered(backend) {
		return serveDatabase{source: serveSourceStore, backend: backend}, nil
	}
	if isEmbeddedMode() {
		return serveDatabase{}, errServeEmbedded()
	}
	return serveDatabase{source: serveSourceProvider}, nil
}

// errServeReadonly refuses `bd --readonly serve`.
//
// AHEAD OF THE WORKSPACE, deliberately: every server this command builds
// publishes the same operation set, claim included, so the answer cannot depend
// on which database source the workspace resolves to. Putting it here is also
// what makes it one answer rather than two — the two sources degraded
// differently, and both silently.
//

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the file named in the error (the metadata.json path) for valid JSON and fix it.
  2. Restore metadata.json from git or re-run `bd init`/backend setup to regenerate it.
  3. Fix file permissions for the serving user.
  4. Inspect the wrapped err via errors.Unwrap/errors.Is for the exact I/O or parse cause.

Example fix

// after failure, inspect and repair:
python3 -m json.tool .beads/metadata.json  # show parse error
git checkout -- .beads/metadata.json        # or fix manually, then retry bd serve
Defensive patterns

Strategy: try-catch

Validate before calling

if _, err := os.Stat(configfile.ConfigPath(beadsDir)); err != nil {
    // metadata.json missing entirely — run bd init or backend setup
}

Try / catch

db, err := serveDatabaseSource(beadsDir)
if err != nil {
    // validate/repair the metadata.json named in the error; never assume embedded fallback
    return err
}

Prevention

When it happens

Trigger: `bd serve` in a workspace where .beads/metadata.json is corrupt, malformed JSON, unreadable, or locked; ConfigPath(beadsDir) points at a file the process cannot read.

Common situations: Hand-edited metadata.json with invalid JSON; interrupted write left a truncated metadata.json; permission issues after cloning a repo as another user; concurrent writes corrupting the file.

Related errors


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