golang/go · error
%s exists but is not a directory
Error message
%s exists but is not a directory
What it means
In local mode, newVCSRepo os.Stats the remote path and requires it to be a directory. If the path exists but is a regular file (or symlink to a file, socket, etc.), this error fires. The %s is the remote path string.
Source
Thrown at src/cmd/go/internal/modfetch/codehost/vcs.go:112
}
func newVCSRepo(ctx context.Context, vcs, remote string, local bool) (Repo, error) {
if vcs == "git" {
return newGitRepo(ctx, remote, local)
}
r := &vcsRepo{remote: remote, local: local}
cmd := vcsCmds[vcs]
if cmd == nil {
return nil, fmt.Errorf("unknown vcs: %s %s", vcs, remote)
}
r.cmd = cmd
if local {
info, err := os.Stat(remote)
if err != nil {
return nil, err
}
if !info.IsDir() {
return nil, fmt.Errorf("%s exists but is not a directory", remote)
}
r.dir = remote
r.mu.Path = r.dir + ".lock"
return r, nil
}
if !strings.Contains(remote, "://") {
return nil, fmt.Errorf("invalid vcs remote: %s %s", vcs, remote)
}
var err error
r.dir, r.mu.Path, err = WorkDir(ctx, vcsWorkDirType+vcs, r.remote)
if err != nil {
return nil, err
}
if cmd.init == nil {
return r, nil
}
View on GitHub (pinned to b6b368adc5)
Solutions
- Point the local-mode path at the actual repository working directory (the folder containing the .hg/.svn/.fossil metadata).
- If using a replace directive, ensure the target is a directory: `replace example.org/m => ./local/m` not a file.
- Remove the stale file at the path and clone the repo there properly.
Example fix
// before replace example.org/m => ./local/m.tar // error: ./local/m.tar exists but is not a directory // after — extract or clone to a directory replace example.org/m => ./local/m
Defensive patterns
Strategy: validation
Validate before calling
func isLocalRepoDir(path string) bool {
info, err := os.Stat(path)
return err == nil && info.IsDir()
} Prevention
- Double-check replace directives point at directories, not files.
- Add a pre-flight `os.Stat` + `IsDir` check in any tool that constructs a local-mode codehost.Repo.
When it happens
Trigger: newVCSRepo called with local==true and remote pointing at a non-directory filesystem entry — e.g. a file path, a stale lock file, or a symlink to a file.
Common situations: Passing a file path instead of a repo directory to a local-mode codehost constructor; a misconfigured GOFLAGS or replace directive that resolves to a file; a leftover `.lock` or tarball where a working copy was expected.
Related errors
- not a directory
- %s exists but is not a directory
- unknown vcs: %s %s
- invalid vcs remote: %s %s
- could not locate sumdb file: missing $GOPATH: %s
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/7ab3e156d988469d.
Report an issue: GitHub.