gohugoio/hugo · error

failed to marshal params: %w

Error message

failed to marshal params: %w

What it means

Returned when json.Marshal(params) fails while preparing the @params virtual module for the esbuild params plugin. The wrapped error is the json package's error and identifies the offending field.

Source

Thrown at internal/js/esbuild/resolve.go:355

					return api.OnLoadResult{
						ResolveDir: opts.ResolveDir,
						Contents:   &c,
						Loader:     opts.loaderFromFilename(args.Path),
					}, nil
				})
		},
	}

	params := opts.Params
	if params == nil {
		// This way @params will always resolve to something.
		params = make(map[string]any)
	}

	b, err := json.Marshal(params)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal params: %w", err)
	}

	paramsPlugin := api.Plugin{
		Name: "hugo-params-plugin",
		Setup: func(build api.PluginBuild) {
			build.OnResolve(api.OnResolveOptions{Filter: `^@params(/config)?$`},
				func(args api.OnResolveArgs) (api.OnResolveResult, error) {
					resolvedPath := args.Importer

					if args.Path == pathHugoConfigParams {
						resolvedPath = pathHugoConfigParams
					}

					return api.OnResolveResult{
						Path:      resolvedPath,
						Namespace: nsHugoParams,
					}, nil
				})

View on GitHub (pinned to 52c9bd7908)

Solutions

  1. Reduce Params to plain JSON-friendly types (string, number, bool, slice, map).
  2. Inspect the wrapped error for the offending field name/type.
  3. Avoid passing Hugo Page/Resource objects; pass primitive values derived from them.

Example fix

// before
{{ $opts := dict "params" . }}

// after
{{ $opts := dict "params" (dict "title" .Title "url" .Permalink) }}
Defensive patterns

Strategy: validation

Validate before calling

// Pre-marshal Params yourself so the json error surfaces with full context
// before Build wraps it.
if opts.Params != nil {
    if _, err := json.Marshal(opts.Params); err != nil {
        return fmt.Errorf("Params not JSON-serializable: %w", err)
    }
}

Prevention

When it happens

Trigger: The Params option contains a value that isn't JSON-serializable: funcs, channels, cyclic structures, or a struct with unexported fields and no custom Marshaler. The marshal at resolve.go:353 fails.

Common situations: User passes a Hugo object (Page, Resource, Site) directly as Params; templating constructs that include non-marshallable Go values; mapstructure decoding produces a map containing a type json.Marshal rejects.

Related errors


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