9.2: gRPC upsert_from_dataframe reports partial failures instead of raising¶
In 9.1 and earlier, GrpcIndex.upsert_from_dataframe raised as soon as any batch
failed. From 9.2 it aggregates, matching upsert(batch_size=...) and the REST
transport, which has behaved this way since v9.0.0.
What changed¶
# 9.1 and earlier — gRPC
try:
index.upsert_from_dataframe(df)
except Exception:
# No way to tell how much landed. Re-run the whole frame.
...
# 9.2 — gRPC and REST
response = index.upsert_from_dataframe(df)
if response.failed_item_count:
for error in response.errors:
print(error.error_message)
index.upsert_from_dataframe(pd.DataFrame(response.failed_items))
UpsertResponse already carried upserted_count, failed_item_count, errors
and failed_items, so there is no new model to learn — the gRPC transport simply
starts populating them.
If you depended on the raise¶
Pass on_error="raise":
index.upsert_from_dataframe(df, on_error="raise")
That re-raises the lowest-indexed batch failure, which is what 9.1 did, with two improvements:
every batch settles before the exception propagates, so nothing is left running server-side;
the partial result is attached to the exception, so the count is no longer lost:
try:
index.upsert_from_dataframe(df, on_error="raise")
except Exception as exc:
print(exc.response.upserted_count)
retry_these = exc.response.failed_items
on_error is a posture choice, not a migration shim — fail-fast on ingest is
legitimate, and it is not going away. It is available on both transports.
Why this is a break we chose to take¶
Both behaviors were already public and depended on: REST has aggregated since v9.0.0, gRPC raised since forever. Partial failure either raises or it does not, so consistency and no-breaking-change were in genuine conflict.
What this gives up is the silence, not the consistency. The blast radius is also smaller than it looks: the old raise discarded the partial count, so no caller relying on it could tell what had landed — a careful gRPC caller was already re-running the whole frame, which is safe either way because upserts are idempotent by vector ID.
Finding out at runtime¶
The first time a gRPC ingest hits a partial failure without an explicit
on_error, the SDK warns once per process, naming response.errors and
on_error="raise". Passing on_error explicitly — either value — silences it.
REST does not warn: its behavior did not change.