golang/go · error

replacement directory %s does not exist

Error message

replacement directory %s does not exist

What it means

A go.mod 'replace' directive points the module at a local filesystem directory, but fsys.Stat reports os.ErrNotExist for that directory. The check exists because dirInModule silently treats missing modules as empty, so without it the later failure would be opaque. The error is wrapped via module.VersionError so it carries the module version identity.

Source

Thrown at src/cmd/go/internal/modload/import.go:809

	if r := Replacement(ld, mod); r.Path != "" {
		if r.Version == "" {
			dir = r.Path
			if !filepath.IsAbs(dir) {
				dir = filepath.Join(replaceRelativeTo(ld), dir)
			}
			// Ensure that the replacement directory actually exists:
			// dirInModule does not report errors for missing modules,
			// so if we don't report the error now, later failures will be
			// very mysterious.
			if _, err := fsys.Stat(dir); err != nil {
				// TODO(bcmills): We should also read dir/go.mod here and check its Go version,
				// and return a gover.TooNewError if appropriate.

				if os.IsNotExist(err) {
					// Semantically the module version itself “exists” — we just don't
					// have its source code. Remove the equivalence to os.ErrNotExist,
					// and make the message more concise while we're at it.
					err = fmt.Errorf("replacement directory %s does not exist", r.Path)
				} else {
					err = fmt.Errorf("replacement directory %s: %w", r.Path, err)
				}
				return dir, true, module.VersionError(mod, err)
			}
			return dir, true, nil
		}
		mod = r
	}

	if mustHaveSums(ld) && !modfetch.HaveSum(ld.Fetcher(), mod) {
		return "", false, module.VersionError(mod, &sumMissingError{})
	}

	dir, err = ld.Fetcher().Download(ctx, mod)
	return dir, false, err
}

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Create the missing directory and ensure it has a go.mod with the expected module path.
  2. Fix the replace path in go.mod to point at the correct relative or absolute location.
  3. Remove or comment out the replace directive if the local override is no longer needed.
  4. If the directory should come from another repo/branch, fetch it first or switch to a versioned replace (=> example.com/foo v1.2.3).

Example fix

// before (go.mod)
replace example.com/foo => ./internal/foo   // ./internal/foo absent
// error: replacement directory ./internal/foo does not exist

// after
mkdir -p internal/foo
cat > internal/foo/go.mod <<EOF
module example.com/foo
go 1.22
EOF
Defensive patterns

Strategy: validation

Validate before calling

// Validate every local replace target exists before building.
data, _ := os.ReadFile("go.mod")
f, _ := modfile.Parse("go.mod", data, nil)
for _, r := range f.Replace {
    if r.New.Version == "" { // local directory replace
        if _, err := os.Stat(r.New.Path); os.IsNotExist(err) {
            return fmt.Errorf("replace target missing: %s", r.New.Path)
        }
    }
}

Try / catch

out, err := exec.Command("go", "build", "./...").CombinedOutput()
if err != nil && bytes.Contains(out, []byte("replacement directory") && bytes.Contains(out, []byte("does not exist"))) {
    // surface the missing path and stop, rather than opaque downstream failures
    return fmt.Errorf("go.mod replace target missing; fix go.mod: %s", out)
}
return err

Prevention

When it happens

Trigger: go.mod contains 'replace example.com/foo => ./local/foo' and ./local/foo does not exist on the working directory / module root. Happens during package import resolution / build when that replacement is consulted.

Common situations: Checked out a fork branch without the local dir; relative replace path authored on a different OS layout; replace target was gitignored or not yet created; monorepo where the sibling module was not generated/built.

Related errors


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