In _eval_factor we have:
# Returns either a 2d ndarray, or a DataFrame, plus is_NA mask
if factor_info.type == "numerical":
result = atleast_2d_column_default(result, preserve_pandas=True)
followed by
if not safe_issubdtype(np.asarray(result).dtype, np.number):
raise PatsyError(
"when evaluating numeric factor %s, "
"I got non-numeric data of type '%s'" % (factor.name(), result.dtype),
factor,
)
When result is a pandas.DataFrame but the factor is not actually a safe numeric type, this code will try to raise an error, but in doing so it attempts to access result.dtype which does not exist.
This is not a critical bug; it just results in some pretty confusing error messages.