gohugoio/hugo · error
error correction level must be one of low, medium…
Error message
error correction level must be one of low, medium, quartile, or high
What it means
Thrown by images.QR when the Level option in the options map is not one of the four allowed keys (low, medium, quartile, high). Hugo looks up opts.Level in the qrErrorCorrectionLevels map (tpl/images/images.go:153) and rejects anything else, including typos, different casing, or numeric levels. The default is "medium" so the error only fires when a user supplies an explicit, invalid Level.
Solutions
- Use one of the exact lowercase keys: low, medium, quartile, or high.
- If the level comes from config, normalize it: {{ $level := lower .Site.Params.qr.level | default "medium" }} and validate against the set.
- Omit Level entirely to accept the default "medium".
- Check the value in front matter / config for typos or trailing whitespace.
Example fix
// before
{{ images.QR .Permalink (dict "Level" "High") }}
// after
{{ images.QR .Permalink (dict "Level" "high") }} Defensive patterns
Strategy: validation
Validate before calling
// Normalize and validate the level before passing to images.QR:
{{ $level := lower (.Param "qr_level" | default "medium") }}
{{ if in (slice "low" "medium" "quartile" "high") $level }}
{{ images.QR .Permalink (dict "Level" $level) }}
{{ end }} Type guard
// Go caller:
var validLevels = map[string]bool{"low": true, "medium": true, "quartile": true, "high": true}
func validLevel(s string) bool { return validLevels[s] } Prevention
- Use lowercase level keys exactly: low, medium, quartile, high.
- Normalize config-derived values with lower and validate against the set.
- Omit Level to rely on the 'medium' default when unsure.
When it happens
Trigger: Calling {{ images.QR "text" (dict "Level" "Medium") }} (wrong case), or "L"/"M"/"Q"/"H" (the qr.Level constants rather than the map keys), or "high1", or a value pulled from a config key that does not match the allowed set.
Common situations: Copying example code that used a different QR library's naming (e.g. "L"/"M"); user-defined config with site params.QRLevel set to an unsupported value; case sensitivity surprises since the map keys are all lowercase.
Related errors
- cannot encode an empty string
- scale must be an integer greater than or equal to 2
- both count and seq must be provided
- cannot index slice/array with nil
- complement needs at least two arguments
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/de65544ea03b0072.
Report an issue: GitHub.
Appendix: source
Thrown at tpl/images/images.go:155
text, err := cast.ToStringE(args[0])
if err != nil {
return nil, err
}
if text == "" {
return nil, errors.New("cannot encode an empty string")
}
if len(args) == 2 {
err := mapstructure.WeakDecode(args[1], &opts)
if err != nil {
return nil, err
}
}
level, ok := qrErrorCorrectionLevels[opts.Level]
if !ok {
return nil, errors.New("error correction level must be one of low, medium, quartile, or high")
}
if opts.Scale < 2 {
return nil, errors.New("scale must be an integer greater than or equal to 2")
}
targetPath := path.Join(opts.TargetDir, fmt.Sprintf("qr_%s.png", hashing.HashStringHex(text, opts)))
r, err := ns.createClient.FromOpts(
create.Options{
TargetPath: targetPath,
TargetPathHasHash: true,
CreateContent: func() (func() (hugio.ReadSeekCloser, error), error) {
code, err := qr.Encode(text, level)
if err != nil {
return nil, err
}
code.Scale = opts.ScaleView on GitHub (pinned to 52c9bd7908)