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
- Check the options against the current `resources.GetRemote` docs; headers must be a map, e.g. `dict "headers" (dict "Authorization" $token)`.
- Inspect the wrapped cause in the error message — it names the offending field — and fix that key's type.
- 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
- Use only documented option keys: method, headers, body, key, timeout — typos and wrong value types fail decoding
- headers must be a map of string to string (or slice of strings), not nested structures
- Build options with dict in one place and reuse via a partial instead of hand-writing them per call
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
- invalid options type: %w
- failed to decode options: %w
- invalid character: %q
- error correction level must be one of low, medium, quartile,
- scale must be an integer greater than or equal to 2
AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31).
Data as JSON: /data/errors/8c6fc630f4a5613a.json.
Report an issue: GitHub ↗.