gohugoio/hugo · error

failed to download module

Error message

failed to download module %s@%s: %w

What it means

Sibling of 460, also in downloadModuleVersion, but fired when extractGoModDownloadError returns an empty string — i.e. `go mod download -json` failed but produced no parseable JSON Error field. The wrapped error is the raw go-command failure (usually an exec.ExitError string from stderr). It signals an opaque go-toolchain failure that Hugo could not annotate further.

Solutions

  1. Reproduce in isolation: run the exact `go mod download -json path@version` from the Hugo working dir and read the raw stderr that Hugo did not parse.
  2. Upgrade the Go toolchain to a current version (`go version`) since older releases emit non-JSON errors Hugo cannot parse.
  3. If stderr shows a sum mismatch, run `go mod tidy` / `hugo mod tidy` to refresh go.sum.
  4. For TLS/proxy HTML errors, fix GOPROXY and network egress, then `go clean -modcache` and retry.

Example fix

// reproduce to see the raw error
// (run from your project root)
go mod download -json github.com/x/y@v1.2.3
// then act on the stderr shown, e.g. update sums:
hugo mod tidy
Defensive patterns

Strategy: validation

Validate before calling

// Probe the exact command Hugo will run and inspect raw stderr
import "os/exec"

func probeDownload(path, version string) error {
  cmd := exec.Command("go", "mod", "download", "-json", path+"@"+version)
  var stderr bytes.Buffer
  cmd.Stderr = &stderr
  if err := cmd.Run(); err != nil {
    if strings.TrimSpace(stderr.String()) != "" {
      return fmt.Errorf("non-json go error: %s", stderr.String())
    }
    return err
  }
  return nil
}

Type guard

null

Try / catch

// Wrap downloadModuleVersion callers:
// m, err := c.downloadModuleVersion(p, v)
// if err != nil && !strings.Contains(err.Error(), "-json") {
//   // likely opaque go failure -> ask user to run go mod download manually
// }

Prevention

When it happens

Trigger: An explicit-version module import where `go mod download -json path@version` exits non-zero with stderr that is not valid JSON or lacks the Error key — e.g. go binary crash, SIGKILL (OOM), TLS handshake error printed to stderr, or a go version that does not emit JSON errors for certain failure classes.

Common situations: Outdated Go binary (<1.16) producing non-JSON output, proxy returning HTML error pages instead of JSON, network resets mid-stream, go.sum verification failures printed only to stderr, or GOFLAGS/GOENV corruption making the toolchain misbehave.

Related errors


AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09). Data as JSON: /api/errors/73f6c49a54951c4c. Report an issue: GitHub.

Appendix: source

Thrown at modules/client.go:466

		if err := json.Unmarshal(b, &cm); err == nil && cm.Dir != "" {
			if c.isDirNonEmpty(cm.Dir) {
				return &cm, nil
			}
		}
	}

	// No valid cache, need to query.
	args := []string{"mod", "download", "-json", fmt.Sprintf("%s@%s", path, version)}

	b := &bytes.Buffer{}
	err := c.runGo(context.Background(), b, args...)
	if err != nil {
		// The -json flag makes Go output error details as JSON to stdout
		// even on failure. Try to extract the error message from the JSON output.
		if jsonErr := extractGoModDownloadError(b.Bytes()); jsonErr != "" {
			return nil, fmt.Errorf("failed to download module %s@%s: %s: %s", path, version, err, jsonErr)
		}
		return nil, fmt.Errorf("failed to download module %s@%s: %w", path, version, err)
	}

	jsonResult := b.Bytes()

	m := &goModule{}
	if err := json.NewDecoder(bytes.NewReader(jsonResult)).Decode(m); err != nil {
		return nil, fmt.Errorf("failed to decode module download result: %w", err)
	}

	if m.Error != nil {
		return nil, errors.New(m.Error.Err)
	}

	// Cache the successful result if it has a Dir field.
	if m.Dir != "" {
		_ = c.ccfg.ModuleQueriesCache.SetBytes(cacheKey, jsonResult)
	}

View on GitHub (pinned to 52c9bd7908)