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

  1. Point the local-mode path at the actual repository working directory (the folder containing the .hg/.svn/.fossil metadata).
  2. If using a replace directive, ensure the target is a directory: `replace example.org/m => ./local/m` not a file.
  3. 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

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


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/7ab3e156d988469d. Report an issue: GitHub.