{"record":{"id":"e8783ed9958e92aa","repo":"gohugoio/hugo","slug":"cannot-encode-an-empty-string","errorCode":null,"errorMessage":"cannot encode an empty string","messagePattern":"cannot encode an empty string","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tpl/images/images.go","lineNumber":143,"sourceCode":"\t\tLevel     string // error correction level; one of low, medium, quartile, or high\n\t\tScale     int    // number of image pixels per QR code module\n\t\tTargetDir string // target directory relative to publishDir\n\t}{\n\t\tLevel: qrDefaultErrorCorrectionLevel,\n\t\tScale: qrDefaultScale,\n\t}\n\n\tif len(args) == 0 || len(args) > 2 {\n\t\treturn nil, errors.New(\"requires 1 or 2 arguments\")\n\t}\n\n\ttext, err := cast.ToStringE(args[0])\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif text == \"\" {\n\t\treturn nil, errors.New(\"cannot encode an empty string\")\n\t}\n\n\tif len(args) == 2 {\n\t\terr := mapstructure.WeakDecode(args[1], &opts)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tlevel, ok := qrErrorCorrectionLevels[opts.Level]\n\tif !ok {\n\t\treturn nil, errors.New(\"error correction level must be one of low, medium, quartile, or high\")\n\t}\n\n\tif opts.Scale < 2 {\n\t\treturn nil, errors.New(\"scale must be an integer greater than or equal to 2\")\n\t}\n","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/gohugoio/hugo/blob/52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2/tpl/images/images.go#L125-L161","documentation":"Thrown by the images.QR template function when the text argument passed to it is an empty string. The QR encoder (rsc.io/qr) has nothing to encode, so Hugo rejects the input explicitly before attempting generation rather than producing a meaningless blank code. The check happens immediately after cast.ToStringE succeeds in tpl/images/images.go:142.","triggerScenarios":"Calling {{ images.QR \"\" }} or {{ images.QR someVar }} where someVar evaluates to an empty string (e.g. an unset page param, an empty .Title, or a scratch/default that resolved to \"\"). Also when the first positional arg is whitespace-only but cast to a trimmed empty string.","commonSituations":"Looping over a collection where some entries have no URL/identifier and generating a QR code per entry; using {{ .Param \"qr_text\" }} without a default; reading from front matter that was left blank; migrating content where a field was renamed and old pages now resolve to empty.","solutions":["Guard with an {{ if qrText }} ... {{ end }} block around the images.QR call so empty values skip generation.","Provide a fallback: {{ $text := .Param \"qr_text\" | default .Permalink }} before calling images.QR.","Filter the source collection with where to exclude entries whose target field is empty before the QR loop.","If the empty value is unexpected, log it ({{ warnf \"empty QR text for %q\" .Path }}) to find which content item is missing data."],"exampleFix":"// before\n{{ $qr := images.QR .Param \"code\" }}\n\n// after\n{{ $code := .Param \"code\" }}\n{{ if $code }}\n  {{ $qr := images.QR $code }}\n{{ end }}","handlingStrategy":"validation","validationCode":"// In a Hugo template, validate before calling images.QR:\n{{ $text := .Param \"qr_text\" }}\n{{ if $text }}\n  {{ $qr := images.QR $text }}\n{{ end }}","typeGuard":"// Go caller using the images.Namespace directly:\nfunc nonEmptyString(v any) (string, bool) {\n    s, ok := v.(string)\n    return s, ok && s != \"\"\n}","tryCatchPattern":null,"preventionTips":["Always gate images.QR behind an {{ if $text }} check.","Provide a default that is guaranteed non-empty (e.g. .Permalink) when the primary source may be blank.","Use {{ warnf }} to surface which content item produced an empty value during the build."],"tags":["hugo","template","qr-code","argument-validation","images"],"backgroundTag":null,"analyzedSha":"52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2","analyzedAt":"2026-08-09T21:49:36.660Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}