gastownhall/beads · error
validateExplicitPath sentinel (ErrNotFound/ErrNotExecutable)
validateExplicitPath sentinel (ErrNotFound/ErrNotExecutable)
Error message
%s=%q: %w
What it means
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.
Source
Thrown at internal/doltversion/resolve.go:123
// what gets returned and probed. This matters for a bare, separator-free
// value like BEADS_DOLT_BIN=dolt-next: os.Stat/exec on a bare name without
// a path separator resolve it two DIFFERENT ways — Go's os/exec (and the
// OS exec syscalls on Unix) treat a name with no separator as a PATH
// lookup, while os.Stat treats it as cwd-relative. Without absolutizing
// first, validateExplicitPath's os.Stat would silently validate a
// cwd-relative file while Probe's later exec.CommandContext(ctx, path,
// ...) would instead search PATH for a same-named binary — approving one
// binary and launching a different one. filepath.Abs forces both steps to
// agree on the same cwd-relative file in that case.
func Resolve(opts ResolveOptions) (string, Source, error) {
if opts.EnvValue != "" {
abs, err := filepath.Abs(opts.EnvValue)
if err != nil {
return "", SourceEnv, fmt.Errorf("%s=%q: resolving absolute path: %w", DoltBinEnvVar, opts.EnvValue, err)
}
abs = completeExecutableExt(abs)
if err := validateExplicitPath(abs); err != nil {
return "", SourceEnv, fmt.Errorf("%s=%q: %w", DoltBinEnvVar, opts.EnvValue, err)
}
return abs, SourceEnv, nil
}
if opts.SidecarValue != "" {
abs, err := filepath.Abs(opts.SidecarValue)
if err != nil {
return "", SourceSidecar, fmt.Errorf("sidecar dolt binary %q: resolving absolute path: %w", opts.SidecarValue, err)
}
abs = completeExecutableExt(abs)
if err := validateExplicitPath(abs); err != nil {
return "", SourceSidecar, fmt.Errorf("sidecar dolt binary %q: %w", opts.SidecarValue, err)
}
return abs, SourceSidecar, nil
}
path, err := exec.LookPath("dolt")
if err != nil {View on GitHub (pinned to 71377f2769)
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
Example fix
// before export BEADS_DOLT_BIN=/opt/tools/dolt # file deleted by upgrade // after export BEADS_DOLT_BIN=/opt/tools/dolt-0.32.1 && chmod +x /opt/tools/dolt-0.32.1
Defensive patterns
Strategy: validation
Validate before calling
if v := os.Getenv("BEADS_DOLT_BIN"); v != "" {
abs, _ := filepath.Abs(v)
if info, err := os.Stat(abs); err != nil || info.IsDir() || info.Mode()&0o111 == 0 {
return fmt.Errorf("BEADS_DOLT_BIN=%q is not a runnable file", v)
}
} Type guard
func isRunnableFile(path string) bool {
info, err := os.Stat(path)
return err == nil && info.Mode().IsRegular() && info.Mode()&0o111 != 0
} Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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.
Related errors
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/b3a660ab1597c4bb.
Report an issue: GitHub.