{"record":{"id":"1c76542334b38ef3","repo":"charmbracelet/crush","slug":"cannot-convert-stop-dir-to-absolute-path-w","errorCode":null,"errorMessage":"cannot convert stop dir to absolute path: %w","messagePattern":"cannot convert stop dir to absolute path: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/fsext/lookup.go","lineNumber":215,"sourceCode":"// traverseUp instead. If stopDir is set but is not an ancestor of dir\n// the walk still stops at the filesystem root, so callers cannot\n// accidentally produce an infinite walk by passing a sibling path.\n//\n// Boundary comparison is performed against symlink-resolved paths so\n// that callers passing logically equivalent paths (a symlinked /var vs\n// the underlying /private/var, for example) still terminate at the\n// expected directory.\nfunc traverseUpBounded(dir, stopDir string, walkFn func(dir string, owner int) error) error {\n\tcwd, err := filepath.Abs(dir)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"cannot convert CWD to absolute path: %w\", err)\n\t}\n\n\tstop := cwd\n\tif stopDir != \"\" {\n\t\tstop, err = filepath.Abs(stopDir)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"cannot convert stop dir to absolute path: %w\", err)\n\t\t}\n\t}\n\tcanonStop := canonicalize(stop)\n\n\towner, err := Owner(dir)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"cannot get ownership: %w\", err)\n\t}\n\n\tfor {\n\t\terr := walkFn(cwd, owner)\n\t\tif err == nil || errors.Is(err, filepath.SkipDir) {\n\t\t\tif canonicalize(cwd) == canonStop {\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\tparent := filepath.Dir(cwd)\n\t\t\tif parent == cwd {","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/fsext/lookup.go#L197-L233","documentation":"When stopDir is non-empty, traverseUpBounded resolves it with filepath.Abs so the bounded walk can compare canonicalized paths. This error wraps a failure of that resolution, again only possible via os.Getwd when stopDir is relative.","triggerScenarios":"Calling LookupBounded/LookupClosestBounded with a relative stopDir (e.g. \"..\" or \"./project\") while the process working directory cannot be determined — deleted CWD, permission loss, or failed mount.","commonSituations":"Computing the project root as a relative path and passing it straight through; running from a removed build directory; containers with dangling CWD.","solutions":["Pass an absolute stopDir (e.g. the resolved project root) to avoid Getwd dependency","Ensure the process CWD is valid before the call, or chdir to a safe directory","Pre-resolve the stop dir yourself with filepath.Abs and handle the error at a more meaningful layer","Restart the process from a valid directory"],"exampleFix":"// before\nfsext.LookupBounded(dir, \"../../project\", targets...)\n// after\nroot, err := filepath.Abs(\"/workspaces/project\")\nif err != nil {\n    return err\n}\nfsext.LookupBounded(dir, root, targets...)","handlingStrategy":"validation","validationCode":"absStop, err := filepath.Abs(stopDir)\nif err != nil {\n    return fmt.Errorf(\"stop dir %q not resolvable: %w\", stopDir, err)\n}\nif _, err := os.Stat(absStop); err != nil {\n    return fmt.Errorf(\"stop dir unavailable: %w\", err)\n}","typeGuard":"func stopDirResolvable(stopDir string) bool {\n    abs, err := filepath.Abs(stopDir)\n    return err == nil && func() bool { _, err := os.Stat(abs); return err == nil }()\n}","tryCatchPattern":"_, err := fsext.LookupBounded(dir, stop, targets...)\nif err != nil {\n    if strings.Contains(err.Error(), \"cannot convert stop dir\") {\n        return fmt.Errorf(\"bad stop dir %q: %w\", stop, err)\n    }\n    return err\n}","preventionTips":["Resolve project roots to absolute paths once at startup and reuse them","Never pass computed relative segments (\"../..\") directly as stopDir","Keep the process CWD valid before any fsext calls","Validate stopDir existence at configuration time"],"tags":["filesystem","path-resolution","cwd"],"backgroundTag":"cwd-resolution-failed","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}