gohugoio/hugo · error
invalid text font source: %T
Error message
invalid text font source: %T
What it means
Thrown by images.Text when the `font` option is not an error but also does not satisfy both required interfaces: hugio.ReadSeekCloserProvider (provides a readable font stream) and resource.Identifier (provides a stable cache key) — see filters.go:118-123. The panic prints the concrete Go type (%T) that was supplied.
Source
Thrown at resources/images/filters.go:122
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()
}
}
}
return filter{
Options: newFilterOpts(text, opt),
Filter: tf,
}
}View on GitHub (pinned to 52c9bd7908)
Solutions
- Load the font via resources.Get (assets/) or resources.GetRemote and pass that resource object as `font`.
- Confirm the resource is non-nil and not an error before use.
- Do not pass raw strings, []byte, or file handles directly.
Example fix
// before
{{ $filters = $filters | append (images.Text "Hi" (dict "font" "assets/fonts/Fira.woff")) }}
// after
{{ $font := resources.Get "fonts/Fira.woff" }}
{{ $filters = $filters | append (images.Text "Hi" (dict "font" $font)) }} Defensive patterns
Strategy: type-guard
Validate before calling
{{ with resources.Get "fonts/Fira.woff" }}
{{ $filters = $filters | append (images.Text "Hi" (dict "font" .)) }}
{{ else }}
{{ errorf "font resource not found" }}
{{ end }} Type guard
// IsFontResource checks the Hugo resource satisfies both interfaces Text requires.
func IsFontResource(v any) bool {
_, a := v.(hugio.ReadSeekCloserProvider)
_, b := v.(resource.Identifier)
return a && b
} Prevention
- Always obtain fonts via resources.Get/GetRemote, never pass raw strings or bytes.
- Confirm the resource is non-nil before use.
- Keep font assets under assets/ and reference them by that path.
When it happens
Trigger: Passing a plain string path, a []byte of font data, an os.File, or any object that is not a Hugo resource as the `font` value. Only Hugo resource objects that expose both interfaces qualify.
Common situations: Developer passes a filesystem path string instead of resources.Get result; passes a remote bytes blob; or passes a resource of a non-font media type that still lacks the required interfaces.
Related errors
- invalid font source: %s
- aligny must be one of top, center, bottom
- %T is not an image filter
- called apply using {xt} as type {targ}
- can't iterate over {type}
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/42efab78b3f169fa.
Report an issue: GitHub.