gohugoio/hugo · error

scale must be an integer greater than or equal to 2

Error message

scale must be an integer greater than or equal to 2

What it means

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.

Solutions

  1. Set Scale to an integer >= 2 (4 is the default and a safe choice).
  2. Clamp from config: {{ $scale := math.Max 2 (.Param "qr_scale" | default 4) }}.
  3. Omit Scale to use the default of 4.
  4. Verify the config value is an integer, not a string or float.

Example fix

// before
{{ images.QR .Permalink (dict "Scale" 1) }}

// after
{{ images.QR .Permalink (dict "Scale" 4) }}
Defensive patterns

Strategy: validation

Validate before calling

// Clamp the scale before calling images.QR:
{{ $scale := .Param "qr_scale" | default 4 }}
{{ if lt $scale 2 }}{{ $scale = 2 }}{{ end }}
{{ images.QR .Permalink (dict "Scale" $scale) }}

Type guard

// Go caller:
func validScale(i int) bool { return i >= 2 }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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

Appendix: source

Thrown at tpl/images/images.go:159

	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.Scale
				png := code.PNG()
				return func() (hugio.ReadSeekCloser, error) {
					return hugio.NewReadSeekerNoOpCloserFromBytes(png), nil
				}, nil

View on GitHub (pinned to 52c9bd7908)