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

score_by=TextQuery(...), not in a list

TypeError: 'TextQuery' object is not iterable

PineconeTypeError saying to wrap the clause in a list

score_by=[clause, 5], an item that is not a clause or a dict

sent to the server, or a misleading PineconeValueError about combining clauses

PineconeTypeError naming the item and the clause classes

top_k or limit that is not an int, such as "5" or 5.0

a bare TypeError, or sent to the server

PineconeTypeError

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 documents.fetch until is_reconciled(target)

exits at once (lsn_committed was None)

never exits; poll documents.search with a deadline

Unknown key in a dict score_by clause

dropped

PineconeValueError

Bare str for ids, include_fields or remove_fields

split into characters

PineconeTypeError

Bare str for a clause’s fields

sent as one string

PineconeTypeError

timeout / total_timeout of zero, negative or NaN

passed through

PineconeValueError

Negative wait timeout other than -1 on create, create-for-model, delete

request sent, then PineconeTimeoutError

PineconeValueError, nothing sent

Non-str or None name or ID

coerced, AttributeError, TypeError or PineconeValueError

PineconeTypeError

API key with whitespace or control characters

first request fails

PineconeValueError at construction

Public index host with http://

silent

RuntimeWarning

additional_headers in a different capitalization

sent alongside the SDK’s header

replaces it

413 in documents.batch_upsert

whole batch failed

halved and resent

413 from any call

ApiError

PayloadTooLargeError (an ApiError)