gohugoio/hugo · error
failed to decode module download result
Error message
failed to decode module download result: %w
What it means
Raised after `go mod download -json` reportedly succeeded (exit 0) but json.NewDecoder could not unmarshal its stdout into the goModule struct. The data Go wrote is not the shape Hugo expects, so the result is unusable. This is almost always a toolchain/output-format skew, not a user config problem.
Solutions
- Confirm the Go binary matches what this Hugo build supports: `go version` and compare against the Hugo release notes for your version.
- Run `go mod download -json path@version` manually and inspect whether stdout is clean JSON (no stray log lines).
- Clear caches: `go clean -modcache && hugo mod clean` then rebuild to rule out cache corruption.
- If a Go wrapper (asdf/gimme) injects output, invoke Hugo with a direct Go binary on PATH.
Example fix
// verify clean JSON output go mod download -json github.com/x/y@v1.2.3 | head // if extra log lines appear, fix the Go wrapper/shell rc, then: hugo mod clean && hugo
Defensive patterns
Strategy: validation
Validate before calling
// Confirm go emits clean, decodable JSON for this module
import ("encoding/json"; "os/exec")
func validateDownloadJSON(path, version string) error {
out, err := exec.Command("go", "mod", "download", "-json", path+"@"+version).Output()
if err != nil { return err }
var m struct{ Dir, Path, Version string }
return json.Unmarshal(out, &m)
} Type guard
null
Try / catch
null
Prevention
- Use the Go version paired with your Hugo release.
- Never let version managers inject stdout during go calls.
- Treat module-cache corruption as a reason to `go clean -modcache`.
When it happens
Trigger: downloadModuleVersion receives non-empty stdout that fails struct decode — e.g. a newer Go emitting additional/renamed fields that break strict decoding, stdout contaminated by a wrapper (gimme, asdf, proxy) prepending logs, or a partial/truncated buffer due to a killed process.
Common situations: Go version far ahead/behind the Hugo release (field mismatches), shell rc files printing output when `go` is invoked, containerised Hugo where stdout is teed to a logger, or a corrupted module cache producing garbled cached JSON.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- failed to decode modules list
- failed to download module
- failed to download module
- failed to download modules
- failed to list modules
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/eaa85c9ef575f6df.
Report an issue: GitHub.
Appendix: source
Thrown at modules/client.go:473
// 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)
}
return m, nil
}
// isDirNonEmpty returns true if dir exists and contains at least one file or subdirectory.
func (c *Client) isDirNonEmpty(dir string) bool {
f, err := c.fs.Open(dir)
if err != nil {View on GitHub (pinned to 52c9bd7908)