gohugoio/hugo · error

failed to decode modules list

Error message

failed to decode modules list: %w

What it means

While streaming `go list -m -json all` output through a json.Decoder loop, a Decode call failed with a non-EOF error. Each module is a separate JSON object, so malformed/truncated output mid-stream trips this. Indicates corrupted or unexpected output from the go toolchain rather than a config issue.

Solutions

  1. Run `go list -m -json all` manually and pipe to `jq` to find where the JSON breaks.
  2. Ensure no shell rc / Go version manager writes to stdout during go invocation.
  3. Upgrade/downgrade Go to a version compatible with your Hugo release.
  4. Clear caches (`go clean -modcache`) and retry to rule out cache corruption.

Example fix

// find the malformed frame
go list -m -json all > /tmp/list.json
jq -c '.' /tmp/list.json   // reports the first bad object
Defensive patterns

Strategy: validation

Validate before calling

// Ensure list output is clean JSON
func ensureListJSON(projectDir string) error {
  cmd := exec.Command("go", "list", "-m", "-json", "all")
  cmd.Dir = projectDir
  out, err := cmd.Output()
  if err != nil { return err }
  dec := json.NewDecoder(bytes.NewReader(out))
  for {
    var m map[string]any
    if err := dec.Decode(&m); err != nil {
      if err == io.EOF { return nil }
      return err
    }
  }
}

Type guard

null

Try / catch

null

Prevention

When it happens

Trigger: go list emitted partially-valid JSON then garbage — interleaved log output, killed process truncating the stream, encoding/whitespace corruption, or a Go version whose list -json shape diverges from what the decoder tolerates.

Common situations: Go wrapper (asdf/gvm) printing to stdout, CI log injection, OOM-killed go process mid-list, or a mismatched ancient Go binary.

Understand the failure class

Related errors


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

Appendix: source

Thrown at modules/client.go:540

		args := []string{"list", "-m", "-json"}
		if len(modules) > 0 {
			args = append(args, modules...)
		} else {
			args = append(args, "all")
		}
		err := c.runGo(context.Background(), b, args...)
		if err != nil {
			return fmt.Errorf("failed to list modules: %w", err)
		}

		dec := json.NewDecoder(b)
		for {
			m := &goModule{}
			if err := dec.Decode(m); err != nil {
				if err == io.EOF {
					break
				}
				return fmt.Errorf("failed to decode modules list: %w", err)
			}

			if err := handle(m); err != nil {
				return err
			}
		}
		return nil
	}

	var modules goModules
	err := listAndDecodeModules(func(m *goModule) error {
		modules = append(modules, m)
		return nil
	})
	if err != nil {
		return nil, err
	}

View on GitHub (pinned to 52c9bd7908)