gohugoio/hugo · error

invalid font source

Error message

invalid font source: %s

What it means

Thrown by images.Text when the `font` option resolves to a value that implements the `error` interface (filters.go:115-117). This indicates the font resource itself failed to load/resolve and the error object was passed through instead of a usable font source, so the filter refuses to proceed.

Solutions

  1. Check the font resource for an error before passing it to images.Text.
  2. Verify the font file exists under assets/ and the path used in resources.Get is correct.
  3. If fetching remotely, confirm the URL returns 200 and a valid font before forwarding it.

Example fix

// before
{{ $f := resources.GetRemote "https://example.com/font.woff" }}
{{ $filters = $filters | append (images.Text "Hi" (dict "font" $f)) }}
// after
{{ $f := resources.GetRemote "https://example.com/font.woff" }}
{{ if $f.Err }}{{ errorf "font load failed: %v" $f.Err }}{{ end }}
{{ $filters = $filters | append (images.Text "Hi" (dict "font" $f)) }}
Defensive patterns

Strategy: validation

Validate before calling

{{ $f := resources.GetRemote "https://example.com/font.woff" }}
{{ with $f.Err }}
  {{ errorf "font fetch failed: %v" . }}
{{ end }}

Type guard

// AsError reports whether v carries a load error (implements error).
func AsError(v any) bool { _, ok := v.(error); return ok }

Prevention

When it happens

Trigger: Passing a resource lookup that returned an error (e.g. a failed resources.Get or a malformed resources.GetRemote result) directly as the `font` argument. The type switch detects the error type first and panics with the underlying error message.

Common situations: Font file is missing from assets/, the path is wrong, GetRemote returned a non-200 response whose error is forwarded, or a resources.Concat/FromString produced an error that was not checked before being handed to Text.

Related errors


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

Appendix: source

Thrown at resources/images/filters.go:116

				tf.x = cast.ToInt(v)
			case "y":
				tf.y = cast.ToInt(v)
			case "alignx":
				tf.alignx = cast.ToString(v)
				if tf.alignx != "left" && tf.alignx != "center" && tf.alignx != "right" {
					panic("alignx must be one of left, center, right")
				}
			case "aligny":
				tf.aligny = cast.ToString(v)
				if tf.aligny != "top" && tf.aligny != "center" && tf.aligny != "bottom" {
					panic("aligny must be one of top, center, bottom")
				}

			case "linespacing":
				tf.linespacing = cast.ToInt(v)
			case "font":
				if err, ok := v.(error); ok {
					panic(fmt.Sprintf("invalid font source: %s", err))
				}
				fontSource, ok1 := v.(hugio.ReadSeekCloserProvider)
				identifier, ok2 := v.(resource.Identifier)

				if !(ok1 && ok2) {
					panic(fmt.Sprintf("invalid text font source: %T", v))
				}

				tf.fontSource = fontSource

				// The input value isn't hashable and will not make a stable key.
				// Replace it with a string in the map used as basis for the
				// hash string.
				opt["font"] = identifier.Key()

			}
		}
	}

View on GitHub (pinned to 52c9bd7908)