gastownhall/beads · error

invalid workspace path: %w

Error message

invalid workspace path: %w

What it means

safeWorkspacePath in cmd/bd/doctor/fix/common.go:103 wraps any failure of filepath.Abs(root) with this error. filepath.Abs essentially only fails when os.Getwd() fails and the root is relative, meaning the process has no readable working directory (e.g. deleted cwd). It indicates the workspace root itself could not be resolved to an absolute path, before any traversal checks run.

Source

Thrown at cmd/bd/doctor/fix/common.go:106

	}

	return dirs.resolved, nil
}

func localWorkspaceBeadsDir(path string) (string, error) {
	absPath, err := filepath.Abs(path)
	if err != nil {
		return "", fmt.Errorf("invalid path: %w", err)
	}
	return filepath.Join(absPath, ".beads"), nil
}

// safeWorkspacePath resolves relPath within the workspace root and ensures it
// cannot escape the workspace via path traversal.
func safeWorkspacePath(root, relPath string) (string, error) {
	absRoot, err := filepath.Abs(root)
	if err != nil {
		return "", fmt.Errorf("invalid workspace path: %w", err)
	}

	cleanRel := filepath.Clean(relPath)
	if filepath.IsAbs(cleanRel) {
		return "", fmt.Errorf("expected relative path, got absolute: %s", relPath)
	}

	joined := filepath.Join(absRoot, cleanRel)
	rel, err := filepath.Rel(absRoot, joined)
	if err != nil {
		return "", fmt.Errorf("failed to resolve path: %w", err)
	}

	if rel == ".." || strings.HasPrefix(rel, ".."+string(os.PathSeparator)) {
		return "", fmt.Errorf("path escapes workspace: %s", relPath)
	}

	return joined, nil

View on GitHub (pinned to 71377f2769)

Solutions

  1. cd to an existing directory (or restart the shell) so os.Getwd() succeeds, then rerun the fix
  2. Pass an absolute workspace root (e.g. /home/me/project) instead of a relative one so filepath.Abs does not need os.Getwd()
  3. Verify the process's cwd exists: run `pwd` or check /proc/<pid>/cwd from another shell
  4. Check filesystem/permission problems that make stat on the cwd fail

Example fix

// before
fix.Do(".")
// after
fix.Do("/home/user/project") // absolute root avoids os.Getwd() dependency
Defensive patterns

Strategy: validation

Validate before calling

root := "/abs/workspace"
if !filepath.IsAbs(root) {
    if _, err := os.Getwd(); err != nil {
        return fmt.Errorf("cwd unavailable: %w", err)
    }
}

Type guard

func hasResolvableRoot(root string) bool {
    _, err := filepath.Abs(root)
    return err == nil
}

Try / catch

p, err := safeWorkspacePath(root, rel)
if err != nil {
    if strings.HasPrefix(err.Error(), "invalid workspace path") {
        // recover: use absolute root or fail fast
    }
    return err
}

Prevention

When it happens

Trigger: Calling safeWorkspacePath with a relative root while the process's current working directory has been deleted or is unreadable, so os.Getwd() inside filepath.Abs returns an error.

Common situations: A bd doctor fix running from a terminal whose cwd was removed (deleted directory, stale shell after rebasing/cleaning), or a daemon/service whose working directory was removed while it processes a relative workspace path.

Related errors


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