gastownhall/beads · error

%s: %w

Error message

%s: %w

What it means

findParentConfig walks up from a start directory looking for a `.beads` config, and wraps any error from configfile.LoadForDiscovery with the candidate config path via `%s: %w`. It exists so users can see exactly which `.beads` config file failed to load during ancestor-directory discovery. The wrapped error typically indicates a corrupt or unreadable config JSON.

Source

Thrown at cmd/bd/bootstrap.go:1058

}

// findParentConfig walks up from beadsDir's parent looking for a
// .beads/metadata.json in ancestor directories. This handles the case where a
// rig subdirectory (its own git repo) doesn't have a local .beads but its
// parent workspace does. Returns nil if no parent config is found. A malformed
// or unreadable ancestor metadata file is authoritative and returned as an error;
// bootstrap must not skip it and select a more distant workspace or defaults.
func findParentConfig(beadsDir string) (*configfile.Config, error) {
	// Start from the parent of beadsDir's enclosing directory.
	// beadsDir is typically "<project>/.beads", so we start from <project>'s parent.
	start := filepath.Dir(filepath.Dir(beadsDir))
	homeDir, _ := os.UserHomeDir()

	for dir := start; dir != "/" && dir != "."; {
		candidate := filepath.Join(dir, ".beads")
		cfg, err := configfile.LoadForDiscovery(candidate)
		if err != nil {
			return nil, fmt.Errorf("%s: %w", configfile.ConfigPath(candidate), err)
		}
		if cfg != nil {
			if err := guardLegacyUpgradeWorkspace(candidate); err != nil {
				return nil, err
			}
			return cfg, nil
		}

		// Don't search above $HOME
		if homeDir != "" && dir == homeDir {
			break
		}

		parent := filepath.Dir(dir)
		if parent == dir {
			break
		}
		dir = parent

View on GitHub (pinned to 71377f2769)

Solutions

  1. Open the path named in the error message and fix or validate the .beads config JSON (e.g. `jq . .beads/config.json`).
  2. If the ancestor config is stale/unwanted, delete or repair that `.beads` directory.
  3. Restore read permissions on the config file (chmod/chown).
  4. Re-run the command; if discovery should skip corrupt ancestors, note existing tests cover TestFindParentConfigDoesNotSkipCorruptNearestAncestor behavior.

Example fix

// before: corrupt config discovered
/home/user/proj/.beads/config.json: unexpected end of JSON input
// after: repair the file
jq . /home/user/proj/.beads/config.json  # find the syntax error, fix it
Defensive patterns

Strategy: try-catch

Validate before calling

if [ -f .beads/config.json ]; then jq -e . .beads/config.json >/dev/null || echo 'corrupt config'; fi

Type guard

func validBeadsConfig(dir string) bool {
	_, err := configfile.LoadForDiscovery(filepath.Join(dir, ".beads"))
	return err == nil
}

Try / catch

cfg, err := findParentConfig(start)
if err != nil {
	var pathErr *os.PathError
	if errors.As(err, &pathErr) { /* handle unreadable path */ }
	return fmt.Errorf("bootstrap discovery failed: %w", err)
}

Prevention

When it happens

Trigger: Running bd commands in (or under) a directory where some ancestor directory has a `.beads/config.json` that fails to load — malformed JSON, unreadable permissions, or a corrupt file — while findParentConfig walks up from the working directory.

Common situations: A partially-written or hand-edited .beads/config.json; a config file truncated by a crashed process or disk-full; wrong file permissions after copying a repo as root; running inside a worktree or subdir where an ancestor's stale config is corrupt.

Related errors


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