gohugoio/hugo · error

failed to decode options for resource %s: %w

Error message

failed to decode options for resource %s: %w

What it means

Inside the cached fetch closure, `resources.GetRemote`'s options map is decoded into a typed struct (headers, method, body, key, responseHeaders, etc.) via `decodeRemoteOptions`. If the map contains keys of the wrong shape or values that can't be mapped onto the expected fields, decoding fails and this wrapped error is returned before any HTTP request is made.

Source

Thrown at resources/resource_factories/create/remote.go:232

		if err != nil {
			return nil, fmt.Errorf("invalid timeout for resource %s: %w", uri, err)
		}
		perRequestTimeout = d
		delete(optionsm, k)
	}

	userKey, optionsKey := remoteResourceKeys(uri, optionsm)

	// A common pattern is to use the key in the options map as
	// a way to control cache eviction,
	// so make sure we use any user provided key as the file cache key,
	// but the auto generated and more stable key for everything else.
	filecacheKey := userKey

	return c.rs.ResourceCache.CacheResourceRemote.GetOrCreate(optionsKey, func(key string) (resource.Resource, error) {
		options, err := decodeRemoteOptions(optionsm)
		if err != nil {
			return nil, fmt.Errorf("failed to decode options for resource %s: %w", uri, err)
		}

		if err := c.validateFromRemoteArgs(uri, options); err != nil {
			return nil, err
		}

		getRes := func() (*http.Response, context.CancelFunc, error) {
			ctx := context.Background()
			var cancel context.CancelFunc
			if perRequestTimeout > 0 {
				ctx, cancel = context.WithTimeout(ctx, perRequestTimeout)
			}
			ctx = c.resourceIDDispatcher.Set(ctx, filecacheKey)

			req, err := options.NewRequest(uri)
			if err != nil {
				if cancel != nil {
					cancel()

View on GitHub ↗ (pinned to 8a468df065)

Solutions

  1. Check the options against the current `resources.GetRemote` docs; headers must be a map, e.g. `dict "headers" (dict "Authorization" $token)`.
  2. Inspect the wrapped cause in the error message — it names the offending field — and fix that key's type.
  3. Print the dict with `warnf "%v"` (redacting secrets) to confirm YAML/TOML produced the structure you expect.

Example fix

{{/* before */}}
{{ $opts := dict "headers" "Authorization: Bearer x" }}
{{/* after */}}
{{ $opts := dict "headers" (dict "Authorization" "Bearer x") }}
Defensive patterns

Strategy: validation

Validate before calling

{{/* keep GetRemote options to known keys with correct shapes */}}
{{ $opts := dict
  "method" "post"
  "headers" (dict "Authorization" (printf "Bearer %s" (getenv "API_TOKEN")))
  "body" (jsonify (dict "q" "hugo"))
}}
{{ $r := resources.GetRemote $url $opts }}

Prevention

When it happens

Trigger: Passing an options dict with malformed values, e.g. `headers` as a plain string instead of a map, `body` as an unsupported type, `responseHeaders` not a string slice, or an unknown-typed value where a string/map is expected in the `resources.GetRemote $url $opts` call.

Common situations: Copying options examples across Hugo versions where the schema changed, building the dict from front matter/data files where YAML produced a different type than expected (map vs list of maps for headers), or misspelling a nested structure so a scalar lands where a map is required.

Related errors


AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31). Data as JSON: /data/errors/8c6fc630f4a5613a.json. Report an issue: GitHub ↗.