gastownhall/beads · error

resolving beads dir %s: %w

Error message

resolving beads dir %s: %w

What it means

ResolvePhysicalRoots wraps filepath.Abs(beadsDir) failures as "resolving beads dir %s: %w". This function canonicalizes the beads directory into absolute physical roots used for gate-file planning; it throws when the OS cannot convert the given beads dir path to an absolute path — rare, and usually caused by extremely long paths (Windows MAX_PATH) or malformed input (empty string leading to getwd failure).

Source

Thrown at internal/doltserver/physical_root.go:250

//     (internal/storage/embeddeddolt), which is precisely where
//     ResolveDoltDir is wrong (it reports beadsDir/dolt) and why this
//     function does not reuse it wholesale.
//  5. no metadata.json: after the shared-server rescue, mirror the open
//     path's discovery fallback (internal/beads findDatabaseInBeadsDir):
//     an embeddeddolt/ dir means embedded, else a dolt/ dir means a
//     server-layout workspace, else default to the embedded root (which
//     may not exist yet — gating a not-yet-existing root is fine, the gate
//     file lives beside it and workspacegate only requires the PARENT to
//     exist). The CLI's nil-config branch honors no other env rescue.
//
// Roots are returned absolute. Symlink canonicalization is deliberately NOT
// performed here: workspacegate.ForPhysicalRoot canonicalizes the gate
// file's parent itself (and refuses symlinked roots), and resolving here too
// would double-handle and could disagree with the gate's own rules.
func ResolvePhysicalRoots(beadsDir string) (PhysicalRoots, error) {
	abs, err := filepath.Abs(beadsDir)
	if err != nil {
		return PhysicalRoots{}, fmt.Errorf("resolving beads dir %s: %w", beadsDir, err)
	}
	abs = filepath.Clean(abs)
	pr := PhysicalRoots{BeadsDir: abs}

	// Side-effect-free config load: never trigger the legacy config.json
	// migration. Absent metadata.json is treated as cfg == nil.
	var cfg *configfile.Config
	if _, statErr := os.Stat(configfile.ConfigPath(abs)); statErr == nil {
		loaded, loadErr := configfile.Load(abs)
		if loadErr != nil {
			// A present-but-broken metadata.json is authoritative: the open
			// path refuses to fall back, so gate planning refuses to guess.
			return PhysicalRoots{}, fmt.Errorf("loading config for gate resolution: %w", loadErr)
		}
		cfg = loaded
	}

	addRoot := func(root string) error {

View on GitHub (pinned to 71377f2769)

Solutions

  1. cd to a valid existing directory before running bd, or pass an explicit --repo/path flag.
  2. Shorten the path (move the workspace closer to the drive root) or enable Windows long paths (LongPathsEnabled registry / git config core.longpaths).
  3. Ensure the beads dir argument passed to ResolvePhysicalRoots is non-empty and well-formed.
  4. Re-create the deleted working directory or restart the shell so getwd returns a valid path.

Example fix

// before
ResolvePhysicalRoots("")            // cwd deleted -> getwd error
// after
abs, _ := filepath.EvalSymlinks(beadsDir)
ResolvePhysicalRoots(abs)           // e.g. "/home/u/proj/.beads"
Defensive patterns

Strategy: validation

Validate before calling

if beadsDir == "" {
    return fmt.Errorf("beadsDir must not be empty")
}
if cwd, err := os.Getwd(); err != nil {
    return fmt.Errorf("current directory unavailable (%v); cd to a valid dir", err)
} else if filepath.IsLocal(beadsDir) {
    if _, err := os.Stat(filepath.Join(cwd, beadsDir)); err != nil {
        return fmt.Errorf("beads dir %s not found", beadsDir)
    }
}

Type guard

func resolvablePath(p string) bool {
    if p == "" { return false }
    if filepath.IsAbs(p) { _, err := os.Stat(p); return err == nil }
    _, err := os.Getwd()
    return err == nil
}

Try / catch

roots, err := ResolvePhysicalRoots(beadsDir)
if err != nil {
    if strings.Contains(err.Error(), "resolving beads dir") {
        return fmt.Errorf("bad --repo path %q or invalid cwd; pass an absolute path", beadsDir)
    }
    return err
}

Prevention

When it happens

Trigger: ResolvePhysicalRoots is called (directly or by tests/startup) with a beadsDir for which filepath.Abs fails: empty string while the current working directory is unavailable (deleted cwd), or a path exceeding OS length limits.

Common situations: Running bd from a directory that was deleted or renamed while the shell sits in it (getwd fails); Windows paths beyond 260 chars without long-path support enabled; passing "" as beadsDir programmatically.

Related errors


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