golang/go · error

error reading module index: %v

Error message

error reading module index: %v

What it means

Recovery error from unprotect() after a panic during large-scale module-index access. It fires only when the recovered value satisfies the addrer interface (a SetPanicOnFault memory-access fault) OR equals the errCorrupt sentinel — i.e. the on-disk index is unreadable or structurally invalid. The wrapped %v is the panic value. If errp is nil the message is passed to base.Fatalf; in tests it is re-panicked.

Source

Thrown at src/cmd/go/internal/modindex/read.go:283

//	defer unprotect(protect, &err)
//
// end looks for panics due to errCorrupt or bad mmap accesses.
// When it finds them, it adds explanatory text, consumes the panic, and sets *errp instead.
// If errp is nil, end adds the explanatory text but then calls base.Fatalf.
func unprotect(old bool, errp *error) {
	// SetPanicOnFault's errors _may_ satisfy this interface. Even though it's not guaranteed
	// that all its errors satisfy this interface, we'll only check for these errors so that
	// we don't suppress panics that could have been produced from other sources.
	type addrer interface {
		Addr() uintptr
	}

	debug.SetPanicOnFault(old)

	if e := recover(); e != nil {
		if _, ok := e.(addrer); ok || e == errCorrupt {
			// This panic was almost certainly caused by SetPanicOnFault or our panic(errCorrupt).
			err := fmt.Errorf("error reading module index: %v", e)
			if errp != nil {
				*errp = err
				return
			}
			if isTest {
				panic(err)
			}
			base.Fatalf("%v", err)
		}
		// The panic was likely not caused by SetPanicOnFault.
		panic(e)
	}
}

// fromBytes returns a *Module given the encoded representation.
func fromBytes(moddir string, data []byte) (m *Module, err error) {
	if !enabled {
		panic("use of index")

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Clear the module cache and let Go rebuild it: `go clean -modcache`.
  2. Re-run the failing command — `go mod download` regenerates the index.
  3. If on a network filesystem, move GOMODCACHE to a local disk.
  4. Verify disk health / free space; check dmesg for filesystem errors.
Defensive patterns

Strategy: fallback

Validate before calling

// Defensive wrapper: if the index read fails, fall back to the live filesystem.
func importWithFallback(modroot, dir string) (*build.Package, error) {
    if pkg, err := modindex.OpenPackage(modroot, dir); err == nil {
        return pkg, nil
    }
    // index unreadable — recompute directly
    return build.ImportDir(filepath.Join(modroot, dir), 0)
}

Try / catch

// Treat index-read errors as recoverable: clear cache once and retry.
pkg, err := modindex.OpenPackage(modroot, dir)
if err != nil && strings.Contains(err.Error(), "error reading module index") {
    _ = os.RemoveAll(filepath.Join(os.Getenv("GOMODCACHE"), "cache/download"))
    pkg, err = modindex.OpenPackage(modroot, dir)
}
return pkg, err

Prevention

When it happens

Trigger: Any index read wrapped by protect()/unprotect() (e.g. fromBytes) when the mmap'd index file is truncated, partially overwritten, or accessed past its end. SetPanicOnFault turns the bad memory access into a recoverable panic that unprotect converts into this error.

Common situations: GOMODCACHE corrupted by a killed `go mod download`, concurrent writers racing the index, disk full mid-write, third-party tools mutating files under the cache, or an OS-level mmap fault on a network filesystem.

Related errors


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