gohugoio/hugo · error

failed to resolve media type for remote resource %q

Error message

failed to resolve media type for remote resource %q

What it means

After fetching, Hugo must assign a media type to the remote resource so it can be processed and given a file suffix. It tries, in order: the Content-Type header (if configured/accepted), extensions derived from the content type, the URL path's file extension, and finally content sniffing via `media.FromContent`. If all of these yield nothing — typically a missing/garbage Content-Type, an extension-less URL, and unrecognizable bytes — Hugo cannot proceed and fails with this error. For HEAD requests there is no body to sniff, making this stricter.

Source

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

				if exts != nil {
					extensionHints = exts
				}
			}

			// Look for a file extension. If it's .txt, look for a more specific.
			if extensionHints == nil || extensionHints[0] == ".txt" {
				if ext := path.Ext(filename); ext != "" {
					extensionHints = []string{ext}
				}
			}

			// Now resolve the media type primarily using the content.
			mediaType = media.FromContent(c.rs.MediaTypes(), extensionHints, body)

		}

		if mediaType.IsZero() {
			return nil, fmt.Errorf("failed to resolve media type for remote resource %q", uri)
		}

		userKey = filename[:len(filename)-len(path.Ext(filename))] + "_" + userKey + mediaType.FirstSuffix.FullSuffix
		data := responseToData(res, false, options.ResponseHeaders)

		return c.rs.NewResource(
			resources.ResourceSourceDescriptor{
				MediaType:     mediaType,
				Data:          data,
				GroupIdentity: identity.StringIdentity(optionsKey),
				LazyPublish:   true,
				OpenReadSeekCloser: func() (hugio.ReadSeekCloser, error) {
					return hugio.NewReadSeekerNoOpCloser(bytes.NewReader(body)), nil
				},
				TargetPath: userKey,
			})
	})
}

View on GitHub ↗ (pinned to 8a468df065)

Solutions

  1. Make the server send a correct `Content-Type` header, or use a URL variant that ends in a real file extension so Hugo can infer from the path.
  2. Add the server's content type to your site config under `mediaTypes` (with a suffix) so `GetByType` resolves it.
  3. If security config filters accepted types, review `security.http.mediaTypes` to ensure the returned Content-Type is allowed.
  4. Avoid HEAD for such endpoints — use GET so content sniffing can run on the body.

Example fix

# config: teach Hugo the server's content type
# before: (no mediaTypes entry, server sends application/vnd.foo)
# after (hugo.toml)
[mediaTypes.'application/vnd.foo']
suffixes = ['foo']
Defensive patterns

Strategy: validation

Validate before calling

{{/* give Hugo something to resolve the type from: extension or explicit mediaType */}}
{{ $r := resources.GetRemote "https://api.example.com/render?id=42" (dict "headers" (dict "Accept" "image/png")) }}
{{/* or force it: */}}
{{ $r := resources.GetRemote $url | resources.Copy "images/chart.png" }}

Prevention

When it happens

Trigger: `resources.GetRemote` against endpoints returning no or bogus `Content-Type` (e.g. `application/octet-stream` for an unknown format) on URLs without a file extension (API routes like `/download?id=123`), HEAD-method requests where the header is the only signal and it maps to no known media type, or content types not present in Hugo's `mediaTypes` configuration and unsniffable content.

Common situations: Download endpoints and signed URLs (S3 presigned links) that omit extensions and serve `octet-stream`, custom or vendor MIME types (`application/vnd.*`) not in Hugo's media type table, and servers misconfigured to send empty Content-Type headers.

Related errors


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