From f7b5afc0f90342dfa88577ffa9d4d4d9f7a6fd78 Mon Sep 17 00:00:00 2001 From: Mateo del Rio Date: Thu, 1 Jan 2026 23:55:33 -0500 Subject: [PATCH] Fix Response Info Assignment for Non-Model Return Types (#564) The API clients were failing when attempting to attach `_response_info` to response data that doesn't support attribute assignment. This could occur when: 1. The `return_data` is an empty string 2. The `return_data` is a type that doesn't support `setattr` (e.g., primitives, immutable types) In the async and sync clients, string responses weren't being handled, causing potential failures when the API returns string data that should have response info attached. Added defensive handling for edge cases when attaching `_response_info`: - Added `json` import for parsing string responses - Added comprehensive string handling: - Empty strings are converted to a dict with `_response_info` - Non-empty strings are parsed as JSON; if successful and the result is a dict, `_response_info` is added - Parse failures fall back to returning a dict with just `_response_info` - Wrapped setattr call in try/except to gracefully handle types that don't support dynamic attribute assignment - Prevents runtime errors when API responses are empty strings or unsupported types - Ensures `_response_info` is available when possible, gracefully degraded when not No breaking changes to existing functionality --- pinecone/openapi_support/api_client.py | 19 ++++++++++++++++++- .../openapi_support/asyncio_api_client.py | 19 ++++++++++++++++++- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/pinecone/openapi_support/api_client.py b/pinecone/openapi_support/api_client.py index afd6b96c..e1163c15 100644 --- a/pinecone/openapi_support/api_client.py +++ b/pinecone/openapi_support/api_client.py @@ -2,6 +2,7 @@ import atexit import io +import json from typing import Any, TYPE_CHECKING @@ -212,9 +213,25 @@ def __call_api( response_info = extract_response_info(headers) if isinstance(return_data, dict): return_data["_response_info"] = response_info + elif isinstance(return_data, str): + if not return_data: + return_data = {"_response_info": response_info} + else: + try: + parsed = json.loads(return_data) + if isinstance(parsed, dict): + parsed["_response_info"] = response_info + return_data = parsed + else: + return_data = {"_response_info": response_info} + except (json.JSONDecodeError, ValueError): + return_data = {"_response_info": response_info} else: # Dynamic attribute assignment on OpenAPI models - setattr(return_data, "_response_info", response_info) + try: + setattr(return_data, "_response_info", response_info) + except (TypeError, AttributeError): + pass if _return_http_data_only: return return_data diff --git a/pinecone/openapi_support/asyncio_api_client.py b/pinecone/openapi_support/asyncio_api_client.py index 58a3a869..92d6c580 100644 --- a/pinecone/openapi_support/asyncio_api_client.py +++ b/pinecone/openapi_support/asyncio_api_client.py @@ -3,6 +3,7 @@ from urllib3.fields import RequestField import orjson +import json from typing import Any @@ -177,9 +178,25 @@ async def __call_api( response_info = extract_response_info(headers) if isinstance(return_data, dict): return_data["_response_info"] = response_info + elif isinstance(return_data, str): + if not return_data: + return_data = {"_response_info": response_info} + else: + try: + parsed = json.loads(return_data) + if isinstance(parsed, dict): + parsed["_response_info"] = response_info + return_data = parsed + else: + return_data = {"_response_info": response_info} + except (json.JSONDecodeError, ValueError): + return_data = {"_response_info": response_info} else: # Dynamic attribute assignment on OpenAPI models - setattr(return_data, "_response_info", response_info) + try: + setattr(return_data, "_response_info", response_info) + except (TypeError, AttributeError): + pass if _return_http_data_only: return return_data