{"record":{"id":"8c6fc630f4a5613a","repo":"gohugoio/hugo","slug":"failed-to-decode-options-for-resource-s-w","errorCode":null,"errorMessage":"failed to decode options for resource %s: %w","messagePattern":"failed to decode options for resource (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"resources/resource_factories/create/remote.go","lineNumber":232,"sourceCode":"\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"invalid timeout for resource %s: %w\", uri, err)\n\t\t}\n\t\tperRequestTimeout = d\n\t\tdelete(optionsm, k)\n\t}\n\n\tuserKey, optionsKey := remoteResourceKeys(uri, optionsm)\n\n\t// A common pattern is to use the key in the options map as\n\t// a way to control cache eviction,\n\t// so make sure we use any user provided key as the file cache key,\n\t// but the auto generated and more stable key for everything else.\n\tfilecacheKey := userKey\n\n\treturn c.rs.ResourceCache.CacheResourceRemote.GetOrCreate(optionsKey, func(key string) (resource.Resource, error) {\n\t\toptions, err := decodeRemoteOptions(optionsm)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to decode options for resource %s: %w\", uri, err)\n\t\t}\n\n\t\tif err := c.validateFromRemoteArgs(uri, options); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tgetRes := func() (*http.Response, context.CancelFunc, error) {\n\t\t\tctx := context.Background()\n\t\t\tvar cancel context.CancelFunc\n\t\t\tif perRequestTimeout > 0 {\n\t\t\t\tctx, cancel = context.WithTimeout(ctx, perRequestTimeout)\n\t\t\t}\n\t\t\tctx = c.resourceIDDispatcher.Set(ctx, filecacheKey)\n\n\t\t\treq, err := options.NewRequest(uri)\n\t\t\tif err != nil {\n\t\t\t\tif cancel != nil {\n\t\t\t\t\tcancel()","sourceCodeStart":214,"sourceCodeEnd":250,"githubUrl":"https://github.com/gohugoio/hugo/blob/52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2/resources/resource_factories/create/remote.go#L214-L250","documentation":"Wrapped error from `Client.FromRemote` (remote.go:229-233) when `decodeRemoteOptions` fails. That helper uses `mapstructure.WeakDecode` (remote.go:448-460) to coerce the options map into the `fromRemoteOptions` struct (`Method`, `Headers`, `Body`, `ResponseHeaders`). Failure means a field has a type that cannot be weakly coerced -- e.g. `headers` set to a string instead of a map, `body` set to a struct, or `method` set to a list.","triggerScenarios":"Calling `resources.GetRemote` with `(dict \"headers\" \"foo: bar\")` (string instead of map), `(dict \"body\" (slice 1 2 3))` (list where bytes/string expected), `(dict \"responseHeaders\" true)` (bool where list expected), or `method` as a number/map.","commonSituations":"Copy-pasting curl-style header strings into the `headers` dict; passing JSON body as a Go map rather than marshalled bytes; misreading docs and providing scalar where collection is required; version drift where option field shapes changed.","solutions":["Provide `headers` as a map of string-to-string/list: `(dict \"headers\" (dict \"Authorization\" \"Bearer x\"))`.","Provide `body` as a string or bytes (use `jsonify` to marshal).","Provide `responseHeaders` as a list of header names to capture.","Validate each option field against `fromRemoteOptions` field types before calling."],"exampleFix":"// template -- before\n{{ $r := resources.GetRemote $url (dict \"headers\" \"Authorization: Bearer x\") }}\n\n// after\n{{ $r := resources.GetRemote $url (dict \"headers\" (dict \"Authorization\" \"Bearer x\")) }}","handlingStrategy":"validation","validationCode":"// Validate option map shape before calling FromRemote.\nfunc validateRemoteOpts(m map[string]any) error {\n\tif h, ok := m[\"headers\"]; ok {\n\t\tif _, ok := h.(map[string]any); !ok {\n\t\t\treturn fmt.Errorf(\"headers must be a map, got %T\", h)\n\t\t}\n\t}\n\tif b, ok := m[\"body\"]; ok {\n\t\tswitch b.(type) {\n\t\tcase string, []byte:\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"body must be string or bytes, got %T\", b)\n\t\t}\n\t}\n\treturn nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["`headers` -> map[string]string/list; `body` -> string/bytes; `responseHeaders` -> []string.","Marshal JSON bodies with `jsonify` before passing.","Cross-check against `fromRemoteOptions` field types."],"tags":["resources","remote","options","validation","config"],"backgroundTag":null,"analyzedSha":"52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2","analyzedAt":"2026-08-09T21:49:36.660Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}