Fix "ValueError: Failed to convert a NumPy array to a Tensor (Unsupported object type float)"

Fix "ValueError: Failed to convert a NumPy array to a Tensor (Unsupported object type float)"

This one usually shows up the first time a Keras model is handed a DataFrame straight out of pd.read_csv() instead of a cleaned-up NumPy array, and the traceback is long enough that the actual cause, an object-dtype array sitting somewhere upstream, is easy to miss under several frames of Keras internals. Below is the reproduction (tested against TensorFlow 2.17.0), what the dtype actually is at the point of failure, and the fix for the two situations that cause it: a mixed-type DataFrame, and a ragged sequence.

Reproducing the Error

The most direct way to trigger it is a DataFrame with one non-numeric column mixed in with numeric ones, converted to a NumPy array and passed straight to model.fit():

import numpy as np
import pandas as pd
import tensorflow as tf

df = pd.DataFrame({
    "age": [25, 31, 42, 29],
    "income": [55000.0, 72000.0, 61000.0, 48000.0],
    "region": ["west", "east", "west", "north"],  # left as strings, not encoded
})
y = np.array([0, 1, 0, 1])

X = df.values
print(X.dtype)  # object

model = tf.keras.Sequential([
    tf.keras.layers.Dense(8, activation="relu"),
    tf.keras.layers.Dense(1, activation="sigmoid"),
])
model.compile(optimizer="adam", loss="binary_crossentropy")
model.fit(X, y, epochs=1)
# ValueError: Failed to convert a NumPy array to a Tensor (Unsupported object type float).

The message is genuinely confusing on first read, it says "Unsupported object type float", not "Unsupported object type str", even though the string column is the actual culprit. That's because by the time TensorFlow inspects the array's elements to explain the failure, it's looking at whatever type happens to be first or most common among the individual Python objects packed into the array, which after pandas's own coercion can easily be a native float object rather than the string that caused the whole array to become object-dtyped in the first place.

Why This Happens: Object Dtype vs. a Real Numeric Dtype

A NumPy array has exactly one dtype for the whole array. When a DataFrame's columns don't share a common numeric type, meaning at least one column is string-typed, boolean mixed with strings, or contains Python objects like lists, calling .values or .to_numpy() doesn't raise an error, it silently upcasts everything to dtype=object, an array of boxed Python objects rather than a packed buffer of float32 or float64 values. That array still prints and slices like a normal array, so nothing looks wrong until it reaches tf.convert_to_tensor(), which requires a single concrete numeric dtype and a rectangular shape to build a Tensor, and raises this ValueError instead of guessing.

The tell: if X.dtype prints object instead of float32/float64/int64, that's the array TensorFlow is going to reject, before it ever gets to check whether your model architecture or shapes are correct.

Cause 1: A Mixed-Type or Unencoded Column Reaching .values

This is the more common case, and the fix is to resolve the non-numeric column before conversion, not to force a cast afterward (casting a string like "west" directly to float will itself raise a separate error). Encode categorical columns first, then convert:

df_encoded = pd.get_dummies(df, columns=["region"])  # or an OrdinalEncoder / LabelEncoder
X = df_encoded.values.astype(np.float32)
print(X.dtype)  # float32, safe to pass to model.fit()

A second, sneakier version of the same cause: a column that looks fully numeric but has a stray non-numeric value baked in as text somewhere down the rows, for example a CSV with an occasional "N/A" or "-" in an otherwise numeric column. That one column being object-typed is enough to upcast the entire array. pd.to_numeric(df[col], errors="coerce") converts the column properly and turns the bad values into NaN, which can then be handled deliberately (dropped or imputed) instead of silently corrupting the array's dtype:

df["income"] = pd.to_numeric(df["income"], errors="coerce")
df = df.dropna(subset=["income"])  # or df["income"].fillna(df["income"].median())

Cause 2: A Ragged Array From Unequal-Length Sequences

The second cause shows up most often in NLP or time-series code, stacking a list of sequences (tokenized sentences, variable-length signal windows) into an array with plain np.array():

sequences = [[1, 5, 9], [2, 3], [7, 1, 4, 8, 2]]  # unequal lengths
X = np.array(sequences)
print(X.dtype)  # object, and NumPy raises/warns about the ragged shape

NumPy can't lay unequal-length rows out as a rectangular numeric buffer, so it falls back to an object array of Python lists, the same failure mode as above, just from shape instead of type. The fix here isn't a cast, since there's no shape to cast into yet, it's padding every sequence to a common length first:

from tensorflow.keras.preprocessing.sequence import pad_sequences

X = pad_sequences(sequences, padding="post", value=0)
print(X.dtype)  # int32, rectangular, ready for an Embedding layer or model.fit()

Diagnosing Which One You Have

Before reaching for either fix, check the dtype and shape of whatever array is actually being passed to model.fit(), right before the call:

print(type(X), getattr(X, "dtype", None), getattr(X, "shape", None))

If the dtype is object and the shape looks like a normal rectangular (n_samples, n_features) tuple, it's the DataFrame/mixed-type case. If X is a plain Python list of arrays, or the shape prints as (n_samples,) with no second dimension when you expected one, it's the ragged case, NumPy only reports a shape for the outer dimension when the inner ones don't agree.

Which One Actually Applies to You

SymptomCauseFix
X.dtype is object, shape is a normal (n, m)Unencoded string/categorical column, or a stray non-numeric value in a numeric columnEncode categoricals with pd.get_dummies/an encoder, clean bad values with pd.to_numeric(errors="coerce"), then .astype(np.float32)
X.dtype is object, shape is (n,) only, elements are lists/arrays of different lengthsRagged sequences stacked with plain np.array()pad_sequences() (or manual padding/truncation) to a common length first

Adding dtype=tf.float32 to tf.convert_to_tensor() or model.fit() does not fix either case, the array has to already be a genuine numeric NumPy dtype with a rectangular shape before TensorFlow sees it.