golang/go · error

zip for %s has unexpected file %s

Error message

zip for %s has unexpected file %s

What it means

When the go command verifies a cached module zip it requires every entry name to begin with `{module.Path}@{module.Version}/`. Any entry missing that prefix is reported as 'unexpected file' — a guard against cache poisoning and malformed archives that has the module-hash check downstream.

Source

Thrown at src/cmd/go/internal/modfetch/fetch.go:344

	if err != nil {
		return err
	}

	// Double-check that the paths within the zip file are well-formed.
	//
	// TODO(bcmills): There is a similar check within the Unzip function. Can we eliminate one?
	fi, err := file.Stat()
	if err != nil {
		return err
	}
	z, err := zip.NewReader(file, fi.Size())
	if err != nil {
		return err
	}
	prefix := mod.Path + "@" + mod.Version + "/"
	for _, zf := range z.File {
		if !strings.HasPrefix(zf.Name, prefix) {
			return fmt.Errorf("zip for %s has unexpected file %s", prefix[:len(prefix)-1], zf.Name)
		}
	}

	if err := file.Close(); err != nil {
		return err
	}

	// Hash the zip file and check the sum before renaming to the final location.
	if err := hashZip(f, mod, file.Name(), ziphashfile); err != nil {
		return err
	}
	if err := os.Rename(file.Name(), zipfile); err != nil {
		return err
	}

	// TODO(bcmills): Should we make the .zip and .ziphash files read-only to discourage tampering?

	return nil

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Run go clean -modcache to wipe and rebuild the cache from scratch.
  2. Verify GOMODACHE is not shared with non-Go tooling that writes into it.
  3. Re-run go mod download for the specific module to repopulate just that entry.

Example fix

// before: cache contains stray top-level files
// terminal
//   go clean -modcache
//   go mod download
Defensive patterns

Strategy: validation

Validate before calling

// Confirm cache integrity before relying on it.
//   go mod verify
// In code, validate the prefix invariant on any cached zip you open:
func cacheZipPrefixOK(mod module.Version, names []string) error {
    prefix := mod.Path + "@" + mod.Version + "/"
    for _, n := range names {
        if !strings.HasPrefix(n, prefix) {
            return fmt.Errorf("unexpected entry %q", n)
        }
    }
    return nil
}

Prevention

When it happens

Trigger: fetch.go opens the cached zip, iterates z.File, and finds an entry whose Name does not have the module@version/ prefix. A hand-edited cache, a partial write, or a proxy returning the wrong module triggers it.

Common situations: Stale or partially-written cache after an interrupted download; manual edits to GOMODCACHE; mismatched GOFLAGS/-insecure; disk corruption.

Related errors


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