{"record":{"id":"f1561076723d75e3","repo":"gastownhall/beads","slug":"invalid-workspace-path-w","errorCode":null,"errorMessage":"invalid workspace path: %w","messagePattern":"invalid workspace path: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/doctor/fix/common.go","lineNumber":106,"sourceCode":"\t}\n\n\treturn dirs.resolved, nil\n}\n\nfunc localWorkspaceBeadsDir(path string) (string, error) {\n\tabsPath, err := filepath.Abs(path)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"invalid path: %w\", err)\n\t}\n\treturn filepath.Join(absPath, \".beads\"), nil\n}\n\n// safeWorkspacePath resolves relPath within the workspace root and ensures it\n// cannot escape the workspace via path traversal.\nfunc safeWorkspacePath(root, relPath string) (string, error) {\n\tabsRoot, err := filepath.Abs(root)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"invalid workspace path: %w\", err)\n\t}\n\n\tcleanRel := filepath.Clean(relPath)\n\tif filepath.IsAbs(cleanRel) {\n\t\treturn \"\", fmt.Errorf(\"expected relative path, got absolute: %s\", relPath)\n\t}\n\n\tjoined := filepath.Join(absRoot, cleanRel)\n\trel, err := filepath.Rel(absRoot, joined)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to resolve path: %w\", err)\n\t}\n\n\tif rel == \"..\" || strings.HasPrefix(rel, \"..\"+string(os.PathSeparator)) {\n\t\treturn \"\", fmt.Errorf(\"path escapes workspace: %s\", relPath)\n\t}\n\n\treturn joined, nil","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/doctor/fix/common.go#L88-L124","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["cd to an existing directory (or restart the shell) so os.Getwd() succeeds, then rerun the fix","Pass an absolute workspace root (e.g. /home/me/project) instead of a relative one so filepath.Abs does not need os.Getwd()","Verify the process's cwd exists: run `pwd` or check /proc/<pid>/cwd from another shell","Check filesystem/permission problems that make stat on the cwd fail"],"exampleFix":"// before\nfix.Do(\".\")\n// after\nfix.Do(\"/home/user/project\") // absolute root avoids os.Getwd() dependency","handlingStrategy":"validation","validationCode":"root := \"/abs/workspace\"\nif !filepath.IsAbs(root) {\n    if _, err := os.Getwd(); err != nil {\n        return fmt.Errorf(\"cwd unavailable: %w\", err)\n    }\n}","typeGuard":"func hasResolvableRoot(root string) bool {\n    _, err := filepath.Abs(root)\n    return err == nil\n}","tryCatchPattern":"p, err := safeWorkspacePath(root, rel)\nif err != nil {\n    if strings.HasPrefix(err.Error(), \"invalid workspace path\") {\n        // recover: use absolute root or fail fast\n    }\n    return err\n}","preventionTips":["Always pass absolute workspace roots to path helpers","Check os.Getwd() health in long-lived daemons before filesystem operations","Restart shells after deleting their working directory"],"tags":["go","filesystem","path-resolution"],"backgroundTag":"missing-working-directory","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}