Upgrading from 10.0 to 10.1¶
10.1 removes nothing and deprecates nothing. Most changes move an error
earlier: calls that 10.0.0 sent to the server as given, or quietly reshaped,
now raise before a request goes out. One change breaks working code without
raising: a loop that polls documents.fetch to wait for a write never exits.
If you wait for writes to become visible, start with
Waiting for a write to become visible.
Coming from 9.x? Read Migrating to V10 first, then this
page.
The snippets run in order against one client. movies is a document index
whose schema declares title as a string field with full-text search:
from pinecone import Pinecone
pc = Pinecone(api_key="your-api-key")
index = pc.index("movies")
Contents¶
Waiting for a write to become visible¶
On the Documents API, an upsert, update or delete response now carries
response_info.lsn_committed, and a search response carries
response_info.lsn_reconciled. In 10.0.0 both were always None. That
breaks the read-your-writes loop the 10.0.0 ResponseInfo reference showed:
# 10.0.0 recipe: never exits on 10.1.0
import time
written = index.documents.upsert(
namespace="movies-en", documents=[{"_id": "movie-42", "title": "Arrival"}]
)
target = written.response_info.lsn_committed
while True:
fetched = index.documents.fetch(namespace="movies-en", ids=["movie-42"])
if target is None or fetched.response_info.is_reconciled(target):
break
time.sleep(0.5)
On 10.0.0, target was None, so the loop exited at once without waiting.
On 10.1.0, target is a number, but a fetch response never reports a
reconciled position, so is_reconciled(target) stays False and the loop
runs forever without raising or warning.
Poll documents.search instead, and give the loop a deadline:
import time
from pinecone import TextQuery
written = index.documents.upsert(
namespace="movies-en", documents=[{"_id": "movie-42", "title": "Arrival"}]
)
target = written.response_info.lsn_committed
deadline = time.monotonic() + 30
while target is not None: # None: caught-up cannot be confirmed
found = index.documents.search(
namespace="movies-en",
top_k=1,
score_by=[TextQuery(query="arrival", fields=["title"])],
)
if found.response_info.is_reconciled(target):
break
if time.monotonic() > deadline:
raise TimeoutError(f"search had not reached LSN {target} after 30s")
time.sleep(0.5)
Fetch and search can see a new write at different times, so a fetch that
returns the document does not mean a search will find it. After
documents.batch_upsert, use result.response_info.lsn_committed as the
target; it is the highest position any of the batch’s requests reached.
Documents API arguments are checked before sending¶
A dict score_by clause with a key its type does not define now raises
PineconeValueError. 10.0.0 dropped the key and sent the rest, so an option
such as boost silently did nothing:
index.documents.search(
namespace="movies-en",
top_k=5,
score_by=[{"type": "text", "fields": ["title"], "query": "arrival", "boost": 2}],
)
# PineconeValueError: Object contains unknown field `boost`
A bare str where a list of names belongs now raises PineconeTypeError.
10.0.0 split ids, include_fields and remove_fields into one entry per
character, so ids="movie-42" deleted the IDs "m", "o", "v" and so on.
A clause’s fields given as one string, which 10.0.0 sent as is, is refused
the same way:
index.documents.delete(namespace="movies-en", ids="movie-42")
# PineconeTypeError: ids must be a sequence of strings, not a single str; pass ['movie-42'] to name one entry.
TextQuery(query="arrival", fields="title")
# PineconeTypeError: 'fields' must be a list of field names, not a single str; pass ['title'].
Three more wrong-type arguments now raise PineconeTypeError with a message
that names the fix:
Call |
10.0.0 |
10.1.0 |
|---|---|---|
|
|
|
|
sent to the server, or a misleading |
|
|
a bare |
|
PineconeTypeError is a TypeError, so an except TypeError around these
calls still catches them.
Timeouts must be positive¶
A timeout or total_timeout of zero, a negative number, or NaN now raises
PineconeValueError before anything is sent. This covers the client-level
timeout on Pinecone and AsyncPinecone, the per-call timeout on every
Documents API method, and total_timeout on documents.batch_upsert,
upsert(batch_size=...) and upsert_from_dataframe. 10.0.0 passed these
values through; total_timeout=0, for example, returned a result with every
item failed.
index.documents.upsert(
namespace="movies-en", documents=[{"_id": "movie-42", "title": "Arrival"}], timeout=0
)
# PineconeValueError: timeout must be a positive number of seconds, got 0
None still means the default (no deadline, for total_timeout).
The wait timeout on pc.indexes.create(), create_for_model() and
delete() changed too. A negative value other than -1 now raises
PineconeValueError before the request. 10.0.0 sent the request, so the
index was created or deleted anyway, and then raised PineconeTimeoutError:
pc.indexes.create(
name="movies-v2",
schema={"fields": {"title": {"type": "string", "full_text_search": {}}}},
timeout=-5,
)
# PineconeValueError: timeout must be None (wait indefinitely), -1 (do not wait), or a non-negative number of seconds, got -5. Pass -1 to skip waiting.
Pass -1 to return without waiting, None to wait as long as it takes, or a
positive number of seconds.
Names and IDs must be strings¶
A name or ID the SDK puts into a URL (an index name, a backup ID, an assistant
name or file ID, an admin resource ID) must be a str. Anything else now
raises PineconeTypeError naming the parameter and the type it got, before
any request:
import uuid
pc.backups.list(index_name=1234)
# PineconeTypeError: index_name must be a string, got int
pc.assistants.describe(name=uuid.uuid4())
# PineconeTypeError: name must be a string, got UUID
pc.indexes.describe(None)
# PineconeTypeError: name must be a string, got NoneType
In 10.0.0, backups.list and the assistant methods converted the value with
str() and requested a resource that cannot exist; elsewhere a non-string
escaped as a bare AttributeError or TypeError. The same check covers
string arguments that never reach the URL, such as
collections.create(source=), inference.rerank(query=) and the Admin
client’s create methods, which also raised AttributeError in 10.0.0.
None for a required name raised PineconeValueError in 10.0.0. An
except PineconeValueError (or except ValueError) written for that no
longer catches it; catch PineconeTypeError as well. An empty string still
raises PineconeValueError.
Convert at the call site, with str(uuid_value) or str(numeric_id). An
optional path parameter still accepts None, so
pc.backups.list(index_name=None) lists backups across every index, and
describe_import() and cancel_import() still accept an int ID.
Creating a client¶
An API key that cannot be sent as an HTTP header, such as one with a trailing
newline read from a file, now raises PineconeValueError when you create the
client. The message never repeats the key. 10.0.0 accepted the key, and the
first request failed inside the HTTP library with a message that could
include it:
api_key = "your-api-key\n" # as read from a file
Pinecone(api_key=api_key)
# PineconeValueError: The API key (api_key or PINECONE_API_KEY) is not a valid HTTP header value: it is blank or whitespace-only, has leading or trailing whitespace (such as a newline read from a file), or contains a control or non-ASCII character. Strip the key, for example api_key=key.strip().
pc = Pinecone(api_key=api_key.strip())
GrpcIndex still accepts leading and trailing spaces and tabs, which gRPC can
send, and refuses the rest.
An index host given with http:// now emits a RuntimeWarning, because the
API key and every request travel unencrypted. Loopback and private IP
addresses, localhost, and *.localhost are exempt, so a local emulator
stays quiet. Pass the host with https://, or with no scheme:
pc.index(host="http://movies-abc123.svc.aped-4627-b74a.pinecone.io")
# RuntimeWarning: The index host http://movies-abc123.svc.aped-4627-b74a.pinecone.io uses http, so the API key and every request and response body travel unencrypted.
additional_headers now replaces an SDK header whatever its capitalization.
In 10.0.0 any spelling other than the exact one, such as
"X-Pinecone-Api-Version", was sent alongside the SDK’s own header, so the
server received two values. This client now sends one
X-Pinecone-Api-Version, 2025-10:
pinned = Pinecone(
api_key="your-api-key", additional_headers={"x-pinecone-api-version": "2025-10"}
)
Pinecone() and AsyncPinecone() called with a keyword they do not take
raise PineconeTypeError instead of a plain TypeError. It is a subclass of
TypeError, so existing handlers still catch it.
Bulk document upserts¶
When the server rejects a documents.batch_upsert batch as too large
(HTTP 413), 10.1.0 halves the batch and resends each half, down to single
documents. 10.0.0 recorded the whole batch as failed. Code that retried
result.failed_items with a smaller batch_size can drop that step: a 413
left in failed_items is a single document too large to send. One batch can
now land in part, so several result.errors rows can share a
batch_index.
A 413 from any call now raises PayloadTooLargeError rather than a plain
ApiError. It is a subclass of ApiError, so except ApiError still
catches it.
The 10.0.0 batch_upsert example passed batch_size=100. On an index with
an embed or sparse_embed field, the server takes at most 96 documents per
upsert or update request, so keep batch_size at 96 or below there, or leave
the default of 50:
index.documents.batch_upsert(
namespace="movies-en",
documents=[{"_id": f"movie-{i}", "title": f"Movie {i}"} for i in range(500)],
batch_size=96,
)
Summary¶
Change |
Before (10.0.0) |
Now (10.1.0) |
|---|---|---|
Polling |
exits at once ( |
never exits; poll |
Unknown key in a dict |
dropped |
|
Bare |
split into characters |
|
Bare |
sent as one string |
|
|
passed through |
|
Negative wait |
request sent, then |
|
Non- |
coerced, |
|
API key with whitespace or control characters |
first request fails |
|
Public index host with |
silent |
|
|
sent alongside the SDK’s header |
replaces it |
413 in |
whole batch failed |
halved and resent |
413 from any call |
|
|