{"record":{"id":"b3a660ab1597c4bb","repo":"gastownhall/beads","slug":"validateexplicitpath-sentinel-errnotfound-errnote","errorCode":"validateExplicitPath sentinel (ErrNotFound/ErrNotExecutable)","errorMessage":"%s=%q: %w","messagePattern":"(.+?)=%q: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/doltversion/resolve.go","lineNumber":123,"sourceCode":"// what gets returned and probed. This matters for a bare, separator-free\n// value like BEADS_DOLT_BIN=dolt-next: os.Stat/exec on a bare name without\n// a path separator resolve it two DIFFERENT ways — Go's os/exec (and the\n// OS exec syscalls on Unix) treat a name with no separator as a PATH\n// lookup, while os.Stat treats it as cwd-relative. Without absolutizing\n// first, validateExplicitPath's os.Stat would silently validate a\n// cwd-relative file while Probe's later exec.CommandContext(ctx, path,\n// ...) would instead search PATH for a same-named binary — approving one\n// binary and launching a different one. filepath.Abs forces both steps to\n// agree on the same cwd-relative file in that case.\nfunc Resolve(opts ResolveOptions) (string, Source, error) {\n\tif opts.EnvValue != \"\" {\n\t\tabs, err := filepath.Abs(opts.EnvValue)\n\t\tif err != nil {\n\t\t\treturn \"\", SourceEnv, fmt.Errorf(\"%s=%q: resolving absolute path: %w\", DoltBinEnvVar, opts.EnvValue, err)\n\t\t}\n\t\tabs = completeExecutableExt(abs)\n\t\tif err := validateExplicitPath(abs); err != nil {\n\t\t\treturn \"\", SourceEnv, fmt.Errorf(\"%s=%q: %w\", DoltBinEnvVar, opts.EnvValue, err)\n\t\t}\n\t\treturn abs, SourceEnv, nil\n\t}\n\n\tif opts.SidecarValue != \"\" {\n\t\tabs, err := filepath.Abs(opts.SidecarValue)\n\t\tif err != nil {\n\t\t\treturn \"\", SourceSidecar, fmt.Errorf(\"sidecar dolt binary %q: resolving absolute path: %w\", opts.SidecarValue, err)\n\t\t}\n\t\tabs = completeExecutableExt(abs)\n\t\tif err := validateExplicitPath(abs); err != nil {\n\t\t\treturn \"\", SourceSidecar, fmt.Errorf(\"sidecar dolt binary %q: %w\", opts.SidecarValue, err)\n\t\t}\n\t\treturn abs, SourceSidecar, nil\n\t}\n\n\tpath, err := exec.LookPath(\"dolt\")\n\tif err != nil {","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/doltversion/resolve.go#L105-L141","documentation":"Resolve wraps a sentinel error (ErrNotFound or ErrNotExecutable) from validateExplicitPath when the explicitly pinned BEADS_DOLT_BIN value fails validation — the path does not exist, is not a regular file, or lacks an executable bit. Per the library's contract, an explicit override is an error rather than a silent fallthrough to sidecar or PATH, because an operator who pins a binary wants a hard failure, not a substituted one. The wrapped sentinel lets callers errors.Is() for a typed diagnosis.","triggerScenarios":"Calling Resolve with ResolveOptions.EnvValue set to a path that fails validateExplicitPath: the file does not exist (ErrNotFound), resolves via symlink to nothing, is a directory or non-regular file, or has no executable bit on Unix (ErrNotExecutable).","commonSituations":"Typo'd BEADS_DOLT_BIN path; pointing at the extensionless name on Windows where only dolt.exe exists; pinning a binary that was later deleted or moved during an upgrade; chmod'ing a downloaded binary without +x; setting the var to a directory path.","solutions":["Check the path spelled in BEADS_DOLT_BIN actually exists (ls the exact value)","Add the executable bit on Unix: chmod +x <path>","On Windows, either spell the .exe extension or ensure the file exists so completeExecutableExt can complete it; if extensionless, remove it or name the .exe directly","If the value was set as a temporary override, unset BEADS_DOLT_BIN to fall back to sidecar/PATH resolution","errors.Is the returned error for doltversion.ErrNotFound vs ErrNotExecutable to distinguish missing vs unusable"],"exampleFix":"// before\nexport BEADS_DOLT_BIN=/opt/tools/dolt   # file deleted by upgrade\n// after\nexport BEADS_DOLT_BIN=/opt/tools/dolt-0.32.1 && chmod +x /opt/tools/dolt-0.32.1","handlingStrategy":"validation","validationCode":"if v := os.Getenv(\"BEADS_DOLT_BIN\"); v != \"\" {\n    abs, _ := filepath.Abs(v)\n    if info, err := os.Stat(abs); err != nil || info.IsDir() || info.Mode()&0o111 == 0 {\n        return fmt.Errorf(\"BEADS_DOLT_BIN=%q is not a runnable file\", v)\n    }\n}","typeGuard":"func isRunnableFile(path string) bool {\n    info, err := os.Stat(path)\n    return err == nil && info.Mode().IsRegular() && info.Mode()&0o111 != 0\n}","tryCatchPattern":null,"preventionTips":["Use absolute paths in BEADS_DOLT_BIN","chmod +x the pinned binary after download","Re-verify the pin after every dolt upgrade","errors.Is(err, doltversion.ErrNotFound) vs ErrNotExecutable to branch handling"],"tags":["go","dolt","binary-resolution","env-var","sentinel-error"],"backgroundTag":"pinned-binary-path-invalid","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}