Skip to content

fix(index): teach the keyword-only call shape when upsert_records gets positional args#694

Open
joerg84 wants to merge 2 commits into
mainfrom
fix/upsert-records-kwargs-691
Open

fix(index): teach the keyword-only call shape when upsert_records gets positional args#694
joerg84 wants to merge 2 commits into
mainfrom
fix/upsert-records-kwargs-691

Conversation

@joerg84

@joerg84 joerg84 commented Jul 17, 2026

Copy link
Copy Markdown

Problem

index.upsert_records("ns", records) fails with the interpreter's bare TypeError: Index.upsert_records() takes 1 positional argument but 3 were given — naming neither the parameters nor the fix. The keyword-only contract is deliberate and stays (parameter order changed across SDK generations, so accepting positionals would silently mis-bind old-style calls); the failure just needs to say so.

Fix

New reject_positional_args(example) guard in _internal/validation.py, applied to the sync, async, and gRPC upsert_records:

PineconeTypeError: upsert_records() accepts keyword arguments only: upsert_records(namespace="...", records=[...]). Positional arguments are rejected because the parameter order changed across SDK generations.
  • PineconeTypeError subclasses TypeError, so existing except TypeError handlers (and the existing test_upsert_records_keyword_only test) keep working unchanged.
  • functools.wraps preserves __wrapped__, so inspect.signature() still reports the real keyword-only signature — verified by a new test.
  • The guard is reusable for other keyword-only methods if the pattern proves useful.

Testing

  • New unit tests for sync/async/gRPC positional misuse (message content) + signature introspection.
  • uv run pytest tests/unit: 4603 passed (socket_options linux/darwin failures are pre-existing on macOS main, untouched).
  • ruff check, ruff format --check, mypy --strict clean on all touched files.

Fixes #691

Found by a cold-agent audit: 2/2 SDK-path agents guessed the positional form and burned 3–4 turns on the bare TypeError; one read the SDK source to find the contract.

🤖 Generated with Claude Code


Note

Low Risk
Behavior change is limited to error messaging for invalid call sites; successful keyword-only calls are unchanged and PineconeTypeError remains a TypeError subclass for existing handlers.

Overview
Adds a reusable reject_positional_args decorator in pinecone/_internal/validation.py and applies it to upsert_records on sync Index, AsyncIndex, and GrpcIndex.

When callers pass extra positional arguments (e.g. upsert_records("ns", records)), the SDK now raises PineconeTypeError with the correct keyword-only example and a note that parameter order changed across SDK generations—instead of Python’s generic “takes 1 positional argument but N were given.” The wrapper still allows only self positionally, preserves inspect.signature via functools.wraps, and for async methods fails at call time (before a coroutine is created); on Python 3.12+ async wrappers are marked for inspect.iscoroutinefunction.

Unit tests cover message content for sync/async/gRPC, async call-time behavior, and keyword-only signature introspection on Index.upsert_records.

Reviewed by Cursor Bugbot for commit 3ac800e. Bugbot is set up for automated code reviews on this repo. Configure here.

…s positional args

upsert_records(ns, records) previously failed with the interpreter's bare
'takes 1 positional argument but 3 were given', which names neither the
parameters nor the fix. The keyword-only contract is deliberate (parameter
order changed across SDK generations, so positionals would silently
mis-bind); the failure just needs to say so. Add a reject_positional_args
guard that raises PineconeTypeError (a TypeError subclass) carrying the
exact call shape, applied to the sync, async, and gRPC variants.
inspect.signature still reports the real keyword-only signature via
__wrapped__.

Fixes #691

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread pinecone/_internal/validation.py Outdated
@joerg84
joerg84 requested a review from jhamon July 17, 2026 13:47
…methods

The async branch wrapped with 'async def', deferring the guard to await
time - an un-awaited misuse returned a coroutine and a 'never awaited'
warning instead of the error, where native keyword-only binding raises
at the call. Use one plain wrapper for both cases (returning the
coroutine for async methods) so the check runs at call time, and mark
the wrapper via inspect.markcoroutinefunction on 3.12+ so
iscoroutinefunction still reports correctly. Flagged by Cursor Bugbot.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 3ac800e. Configure here.

return fn(*args, **kwargs)

if sys.version_info >= (3, 12) and inspect.iscoroutinefunction(fn):
inspect.markcoroutinefunction(wrapper)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coroutine detection broken on older Python

Low Severity

reject_positional_args only applies inspect.markcoroutinefunction on Python 3.12+, so on supported 3.10/3.11 inspect.iscoroutinefunction and asyncio.iscoroutinefunction report False for AsyncIndex.upsert_records. Callers that branch on that check can treat the method as sync and mishandle the returned coroutine.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 3ac800e. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

upsert_records(ns, records) fails with a bare TypeError — should teach the keyword-only call shape

1 participant