stretchr/testify · error

Parameters must be numerical

Error message

Parameters must be numerical

What it means

Returned by calcRelativeError (assert/assertions.go:1554) when either toFloat(expected) or toFloat(actual) returns false. toFloat (assert/assertions.go:1413) only accepts numeric kinds (int/uint/float variants) and time.Duration; anything else (string, struct, bool, slice) fails. The error surfaces via assert.InEpsilon which calls calcRelativeError.

Source

Thrown at assert/assertions.go:1558

		if !InDelta(
			t,
			ev.Interface(),
			av.Interface(),
			delta,
			msgAndArgs...,
		) {
			return false
		}
	}

	return true
}

func calcRelativeError(expected, actual interface{}) (float64, error) {
	af, aok := toFloat(expected)
	bf, bok := toFloat(actual)
	if !aok || !bok {
		return 0, fmt.Errorf("Parameters must be numerical")
	}
	if math.IsNaN(af) && math.IsNaN(bf) {
		return 0, nil
	}
	if math.IsNaN(af) {
		return 0, errors.New("expected value must not be NaN")
	}
	if af == 0 {
		return 0, fmt.Errorf("expected value must have a value other than zero to calculate the relative error")
	}
	if math.IsNaN(bf) {
		return 0, errors.New("actual value must not be NaN")
	}

	return math.Abs(af-bf) / math.Abs(af), nil
}

// InEpsilon asserts that expected and actual have a relative error less than epsilon

View on GitHub (pinned to 001eb7946b)

Solutions

  1. Convert both operands to a numeric type (float64/int) before passing them to InEpsilon.
  2. If the value comes from JSON, unmarshal into a typed float64 variable rather than interface{}.
  3. Switch to assert.Equal if exact comparison is acceptable and the value is non-numeric.

Example fix

// before
assert.InEpsilon(t, jsonVal, expected, 0.01) // jsonVal is interface{}
// after
f, _ := jsonVal.(float64)
assert.InEpsilon(t, f, expected, 0.01)
Defensive patterns

Strategy: type-guard

Validate before calling

func isNumeric(v interface{}) bool {
    switch v.(type) {
    case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64, time.Duration:
        return true
    }
    return false
}
// before InEpsilon:
if !isNumeric(expected) || !isNumeric(actual) { t.Fatal("non-numeric") }

Type guard

func isNumeric(v interface{}) bool {
    switch v.(type) {
    case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64, time.Duration:
        return true
    }
    return false
}

Try / catch

// Validate types before the call (Go returns this as error via Fail, not panic):
if !isNumeric(expected) || !isNumeric(actual) {
    t.Fatalf("InEpsilon needs numeric args, got %T, %T", expected, actual)
}
assert.InEpsilon(t, expected, actual, eps)

Prevention

When it happens

Trigger: Calling assert.InEpsilon(t, expected, actual, epsilon) or assert.InEpsilonSlice where expected or actual is a string, struct, bool, or any non-numeric type. Also triggered by passing a numeric wrapped in an interface{} that lost its concrete numeric type.

Common situations: Asserting tolerance on values read from JSON/JSON-unmarshaled into interface{} (which become float64 but may be string-encoded numbers), or comparing parsed config values that arrived as strings.

Related errors


AI-assisted analysis of stretchr/testify@001eb7946b (2026-08-04). Data as JSON: /data/errors/043d456776da15e9.json. Report an issue: GitHub.