charmbracelet/crush · error

cannot convert CWD to absolute path: %w

Error message

cannot convert CWD to absolute path: %w

What it means

traverseUp (used by Lookup and LookupClosest) converts the starting directory to an absolute path with filepath.Abs before walking. This error is returned when filepath.Abs fails, which happens only when os.Getwd fails while resolving a relative dir.

Source

Thrown at internal/fsext/lookup.go:166

			found = append(found, fpath)
		}

		return nil
	})
	if err != nil {
		return nil, err
	}

	return found, nil
}

// traverseUp walks up from given directory up until filesystem root reached.
// It passes absolute path of current directory and staring directory owner ID
// to callback function. It is up to user to check ownership.
func traverseUp(dir string, walkFn func(dir string, owner int) error) error {
	cwd, err := filepath.Abs(dir)
	if err != nil {
		return fmt.Errorf("cannot convert CWD to absolute path: %w", err)
	}

	owner, err := Owner(dir)
	if err != nil {
		return fmt.Errorf("cannot get ownership: %w", err)
	}

	for {
		err := walkFn(cwd, owner)
		if err == nil || errors.Is(err, filepath.SkipDir) {
			parent := filepath.Dir(cwd)
			if parent == cwd {
				return nil
			}

			cwd = parent
			continue
		}

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Pass an absolute path to Lookup instead of a relative one so filepath.Abs never needs os.Getwd
  2. Restore or recreate the process working directory, or chdir to a valid directory first
  3. Fix permissions on the current directory (chmod +x) or remount a failed filesystem
  4. Restart the process from a valid directory

Example fix

// before
found, err := fsext.Lookup(".", ".git")
// after
absDir, err := filepath.Abs("/project/root") // absolute input avoids Getwd
if err != nil {
    return err
}
found, err := fsext.Lookup(absDir, ".git")
Defensive patterns

Strategy: validation

Validate before calling

abs, err := filepath.Abs(dir) // fails only via Getwd for relative dir
if err != nil {
    return fmt.Errorf("starting dir not resolvable: %w", err)
}
if _, err := os.Stat(abs); err != nil {
    return fmt.Errorf("starting dir unavailable: %w", err)
}

Type guard

func dirIsResolvable(dir string) bool {
    abs, err := filepath.Abs(dir)
    return err == nil && func() bool { _, err := os.Stat(abs); return err == nil }()
}

Try / catch

found, err := fsext.Lookup(dir, targets...)
if err != nil {
    if strings.Contains(err.Error(), "cannot convert CWD to absolute path") {
        // fall back to absolute input path
        if abs, aerr := filepath.Abs(dir); aerr == nil {
            found, err = fsext.Lookup(abs, targets...)
        }
    }
    if err != nil {
        return err
    }
}

Prevention

When it happens

Trigger: Passing a relative path (including "" or ".") to Lookup/LookupClosest while the process working directory is unreadable — e.g. the CWD directory was deleted, lacks execute permission, or is on a failed mount.

Common situations: Running the tool from a directory that was removed by another process (common after builds clean the tree); dropping into a directory without +x for the current user; containers with a deleted working directory.

Related errors


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/f149f5abf91a0d8f. Report an issue: GitHub.