hashicorp/terraform · error

must not be a whole number

Error message

must not be a whole number

What it means

Returned by backendbase.IntValue when the cty.Number value is non-null but its big.Float does not convert to int64 exactly (big.Accuracy != big.Exact). In other words the user supplied a fractional number where the backend schema requires a whole number.

Source

Thrown at internal/backend/backendbase/helper.go:111

		return fallback
	}
	return ret
}

// IntValue converts a cty value into a Go int64, or returns an error if that's
// not possible.
func IntValue(v cty.Value) (int64, error) {
	v, err := convert.Convert(v, cty.Number)
	if err != nil {
		return 0, err
	}
	if v.IsNull() {
		return 0, fmt.Errorf("must not be null")
	}
	bf := v.AsBigFloat()
	ret, acc := bf.Int64()
	if acc != big.Exact {
		return 0, fmt.Errorf("must not be a whole number")
	}
	return ret, nil
}

// BoolValue converts a cty value Go bool, or returns an error if that's not
// possible.
func BoolValue(v cty.Value) (bool, error) {
	v, err := convert.Convert(v, cty.Bool)
	if err != nil {
		return false, err
	}
	if v.IsNull() {
		return false, fmt.Errorf("must not be null")
	}
	return v.True(), nil
}

// MustBoolValue converts a cty value Go bool, or panics if that's not possible.

View on GitHub (pinned to c9def3e214)

Solutions

  1. Change the value to a whole number: `retry_wait_max = 3`.
  2. If sub-second precision is needed, check whether the backend supports a finer unit; otherwise round up/down to the nearest integer.
  3. Review the backend's documented schema to confirm the attribute is an integer.
  4. Validate user-supplied config with a 'must be integer' check before init.

Example fix

// before (fractional -> 'must not be a whole number')
backend "http" {
  address       = "https://state.example.com"
  retry_wait_max = 2.5
}
// after
backend "http" {
  address       = "https://state.example.com"
  retry_wait_max = 3
}
Defensive patterns

Strategy: validation

Validate before calling

// Reject fractional numbers before IntValue.
if v.Type() == cty.Number {
    bf := v.AsBigFloat()
    if _, acc := bf.Int64(); acc != big.Exact {
        return errors.New("value must be a whole number")
    }
}

Type guard

func isNonIntegerError(err error) bool {
    return err != nil && err.Error() == "must not be a whole number"
}

Try / catch

n, err := backendbase.IntValue(v)
if err != nil {
    // surface with the attribute name for clarity
    return fmt.Errorf("%s: %w", attrName, err)
}

Prevention

When it happens

Trigger: Returned at internal/backend/backendbase/helper.go:111 when bf.Int64() returns acc != big.Exact. Used by the http backend for retry_max / retry_wait_min / retry_wait_max (and any backend calling IntValue for an int field).

Common situations: Setting `retry_wait_max = 2.5` (seconds) or any decimal where the backend expects an integer count/seconds. Misreading a duration field that actually wants whole seconds. Copy-pasting a float default from docs.

Related errors


AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07). Data as JSON: /api/errors/901a891d1bebf846. Report an issue: GitHub.