{"record":{"id":"b92743088156e165","repo":"gohugoio/hugo","slug":"scale-must-be-an-integer-greater-than-or-equal-to","errorCode":null,"errorMessage":"scale must be an integer greater than or equal to 2","messagePattern":"scale must be an integer greater than or equal to 2","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tpl/images/images.go","lineNumber":159,"sourceCode":"\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\n\ttargetPath := path.Join(opts.TargetDir, fmt.Sprintf(\"qr_%s.png\", hashing.HashStringHex(text, opts)))\n\n\tr, err := ns.createClient.FromOpts(\n\t\tcreate.Options{\n\t\t\tTargetPath:        targetPath,\n\t\t\tTargetPathHasHash: true,\n\t\t\tCreateContent: func() (func() (hugio.ReadSeekCloser, error), error) {\n\t\t\t\tcode, err := qr.Encode(text, level)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tcode.Scale = opts.Scale\n\t\t\t\tpng := code.PNG()\n\t\t\t\treturn func() (hugio.ReadSeekCloser, error) {\n\t\t\t\t\treturn hugio.NewReadSeekerNoOpCloserFromBytes(png), nil\n\t\t\t\t}, nil","sourceCodeStart":141,"sourceCodeEnd":177,"githubUrl":"https://github.com/gohugoio/hugo/blob/52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2/tpl/images/images.go#L141-L177","documentation":"Thrown by images.QR when the Scale option (image pixels per QR module) is less than 2. The default is 4, so this only fires when a user explicitly passes a Scale of 0, 1, or a negative number. A scale below 2 makes the QR code unscannable, so Hugo rejects it at tpl/images/images.go:158.","triggerScenarios":"Calling {{ images.QR \"text\" (dict \"Scale\" 1) }} or passing Scale 0/negative; passing a float like 1.5 that WeakDecode truncates to 1; pulling Scale from a config value set incorrectly.","commonSituations":"Trying to make a tiny QR code for compact layouts and underestimating the minimum; config param mis-typed as a string that decodes to 0; copying a snippet that used a different default.","solutions":["Set Scale to an integer >= 2 (4 is the default and a safe choice).","Clamp from config: {{ $scale := math.Max 2 (.Param \"qr_scale\" | default 4) }}.","Omit Scale to use the default of 4.","Verify the config value is an integer, not a string or float."],"exampleFix":"// before\n{{ images.QR .Permalink (dict \"Scale\" 1) }}\n\n// after\n{{ images.QR .Permalink (dict \"Scale\" 4) }}","handlingStrategy":"validation","validationCode":"// Clamp the scale before calling images.QR:\n{{ $scale := .Param \"qr_scale\" | default 4 }}\n{{ if lt $scale 2 }}{{ $scale = 2 }}{{ end }}\n{{ images.QR .Permalink (dict \"Scale\" $scale) }}","typeGuard":"// Go caller:\nfunc validScale(i int) bool { return i >= 2 }","tryCatchPattern":null,"preventionTips":["Keep Scale >= 2 (4 is the safe default).","Clamp config-derived scale values to a minimum of 2.","Ensure the config value is an integer, not a float or string."],"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"}