{"record":{"id":"e09e086504be3f7b","repo":"gastownhall/beads","slug":"identity-resolve-root-path-w","errorCode":null,"errorMessage":"identity: resolve root path: %w","messagePattern":"identity: resolve root path: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dbproxy/identity/identity.go","lineNumber":34,"sourceCode":"\n// SecretFileName is the per-workspace secret used to authenticate control\n// listener requests.\nconst SecretFileName = \"proxy.secret\"\n\n// RootID returns the SHA-256 of rootDir's symlink-resolved absolute path.\n// It identifies the workspace proxy root, not the Dolt data directory;\n// upstream_id continues to identify the backend through DoltServer.ID.\n// Darwin's default case-insensitive filesystems can resolve the same directory\n// through differently cased path spellings; callers should use a canonical\n// workspace spelling when they need stable IDs across invocations.\nfunc RootID(rootDir string) (string, error) {\n\tabs, err := filepath.Abs(rootDir)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"identity: absolute root path: %w\", err)\n\t}\n\tresolved, err := filepath.EvalSymlinks(abs)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"identity: resolve root path: %w\", err)\n\t}\n\tsum := sha256.Sum256([]byte(resolved))\n\treturn hex.EncodeToString(sum[:]), nil\n}\n\n// WriteSecret creates and atomically writes a new control-listener secret.\n// Each proxy start intentionally rotates the previous secret.\nfunc WriteSecret(rootDir string) (string, error) {\n\traw := make([]byte, 32)\n\tif _, err := rand.Read(raw); err != nil {\n\t\treturn \"\", fmt.Errorf(\"identity: generate proxy secret: %w\", err)\n\t}\n\tsecret := hex.EncodeToString(raw)\n\tif err := atomicfile.WriteFile(filepath.Join(rootDir, SecretFileName), []byte(secret+\"\\n\"), 0o600); err != nil {\n\t\treturn \"\", fmt.Errorf(\"identity: write proxy secret: %w\", err)\n\t}\n\treturn secret, nil\n}","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dbproxy/identity/identity.go#L16-L52","documentation":"RootID resolves the workspace root directory to a stable ID by taking its absolute path, resolving symlinks with filepath.EvalSymlinks, and hashing the result with SHA-256. This error wraps any failure from EvalSymlinks, which fails if any component of the path does not exist or is an unreadable/broken symlink. The library throws it because it cannot compute a canonical path for a root that the filesystem does not resolve.","triggerScenarios":"Calling identity.RootID(rootDir) when rootDir does not exist on disk, when a parent directory in the path is missing, when a symlink in the path is broken (dangling), or when the process lacks permission to traverse a path component (returns *fs.PathError, often os.ErrNotExist or os.ErrPermission).","commonSituations":"Starting the dbproxy against a workspace directory that was never created (typo in BEADS_DIR or --root flag), running after the workspace was deleted or moved, pointing at a symlink whose target was removed, or containers with a partially mounted volume.","solutions":["Create the root directory before calling RootID, e.g. os.MkdirAll(rootDir, 0o755)","Verify the path exists and is traversable: os.Stat(filepath.EvalSymlinks'd absolute path) and check errors.Is(err, fs.ErrNotExist)","Fix or remove broken symlinks in the path chain","Correct the rootDir value (typo, stale env var) to the actual workspace root"],"exampleFix":"// before\nid, err := identity.RootID(rootDir) // fails if rootDir missing\n// after\nif err := os.MkdirAll(rootDir, 0o755); err != nil {\n    return err\n}\nid, err := identity.RootID(rootDir)","handlingStrategy":"validation","validationCode":"if _, err := os.Stat(rootDir); err != nil {\n    return fmt.Errorf(\"workspace root %q unavailable: %w\", rootDir, err)\n}\nif _, err := filepath.EvalSymlinks(rootDir); err != nil {\n    return fmt.Errorf(\"cannot resolve %q: %w\", rootDir, err)\n}","typeGuard":null,"tryCatchPattern":"id, err := identity.RootID(rootDir)\nif err != nil {\n    var perr *fs.PathError\n    if errors.As(err, &perr) && errors.Is(perr.Err, fs.ErrNotExist) {\n        // create rootDir or surface a clear setup error\n    }\n    return err\n}","preventionTips":["Create the workspace root with os.MkdirAll before any dbproxy identity calls","Validate the rootDir flag/env value at startup","Watch for broken symlinks when workspaces are moved between machines","Run the proxy as a user with traverse permission on all path components"],"tags":["go","filesystem","symlink","path-resolution"],"backgroundTag":"path-not-found","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}