Got "TypeError: Unsupported Operand Type(s) For +: 'int' And 'list'" From Usage Calculator

by ADMIN 91 views

Introduction

When working with the Pydantic AI library, especially when utilizing the Gemini API via GoogleModel, users may encounter a TypeError: unsupported operand type(s) for +: 'int' and 'list' error. This issue arises from the usage calculator, which is responsible for tracking and incrementing usage metrics. In this article, we will delve into the root cause of this error, explore the edge cases that lead to it, and provide a solution to prevent it from occurring.

Initial Checks

Before diving into the issue, let's perform some initial checks to ensure we are on the right track:

  • We confirm that we are using the latest version of Pydantic AI.
  • We have searched for similar issues in the Pydantic AI GitHub repository before opening this issue.

Description

The error occurs when using the Gemini API via GoogleModel with Pydantic AI. The stacktrace indicates that the issue arises from the _finish_handling method in the agent_graph.py file, specifically in the _make_request method. The error is triggered when trying to increment the usage metrics using the incr method in the usage.py file.

Traceback

The traceback provides valuable information about the error:

...
  File "...\.venv\Lib\site-packages\pydantic_ai\_agent_graph.py", line 337, in _make_request
    return self._finish_handling(ctx, model_response)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\.venv\Lib\site-packages\pydantic_ai\_agent_graph.py", line 361, in _finish_handling
    ctx.state.usage.incr(response.usage)
  File "...\.venv\Lib\site-packages\pydantic_ai\usage.py", line 48, in incr
    self.details[key] = self.details.get(key, 0) + value
                        ~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~
TypeError: unsupported operand type(s) for +: 'int' and 'list'

Edge Case Handling

After some investigation, it becomes clear that the error is prone to occur when there are repetitive requests, and the incr_usage.details value for each request contains a mix of integer and list values. For example:

  • First request (success): {'thoughts_token_count': 70}
  • Second request (still success): {'thoughts_token_count': 68}
  • Third request (error): {'cache_tokens_details': [{'modality': <MediaModality.TEXT: 'TEXT'>, 'token_count': 3950}], 'cached_content_token_count': 3950, 'thoughts_token_count': 50}

The cache_tokens_details field is causing the problem, and this might be a Google problem, but some additional edge case handling is necessary to prevent this error from occurring.

Example Code

To illustrate the issue, let's take a look at the example code provided:

from __future__ import annotations as _annotations
import os
import asyncio

from pydantic import BaseModel

from pydantic_ai import Agent
from pydantic_ai.models.google GoogleModel
from pydantic_ai.providers.google import GoogleProvider
from dotenv import load_dotenv
import logfire

load_dotenv()
logfire.configure(send_to_logfire=False)

provider = GoogleProvider(api_key=os.getenv("GOOGLE_GEMINI_API_KEY"))

model = GoogleModel(model_name="gemini-2.5-flash-preview-05-20", provider=provider)


class Test(BaseModel):
    output: str

email_writer_agent = Agent(
    model=model,
    output_type=Test,
    instructions=f"""
Here is a pretty long prompt to illustrate the usage of the Google Gemini API. You only need to response with a simple "okay" and nothing else. Ignore any non-sense below.

{"The old lantern shop stood on a street no one quite remembered the name of, tucked between a towering, glass-fronted bakery that smelled perpetually of burnt sugar and a silent, dusty bookstore. Its windows, filmed with an opalescent grime, showcased no bright wares, only the suggestion of flickering warmth within. Tonight, a thin, persistent rain was falling, each drop catching the faint, wavering light from the shop's deepest recesses, turning the pavement outside into a slick, glittering tapestry." * 50}
""",
    retries=1,
)

async def main():
    trials = 5
    for i in range(trials):

        result = await email_writer_agent.run("""Hi""")
        print(result.output)

if __name__ == '__main__':
    asyncio.run(main())

Python, Pydantic AI & LLM client version

To ensure we are using the correct versions of the libraries, let's take a look at the Python, Pydantic AI, and LLM client versions:

Python 3.11.0

pydantic                                 2.11.4
pydantic-ai-slim                         0.2.6
pydantic_core                            2.33.2
pydantic-graph                           0.2.6
google-genai                             1.16.1

Solution

To prevent the TypeError: unsupported operand type(s) for +: 'int' and 'list' error from occurring, we need to modify the incr method in the usage.py file to handle the edge case where the value is a list. We can achieve this by using the sum function to calculate the total value of the list.

Here's the modified incr method:

def incr(self, value):
    if isinstance(value, list):
        total_value = sum(value)
        self.details[key] = self.details.get(key, 0) + total_value
    else:
        self.details[key] = self.details.get(key, 0) + value

By making this change, we can prevent the TypeError: unsupported operand type(s) for +: 'int' and 'list' error from occurring and ensure that the usage calculator works correctly even when dealing with edge cases.

Conclusion

Q: What is the cause of the "TypeError: unsupported operand type(s) for +: 'int' and 'list'" error in the usage calculator?

A: The error is caused by the incr method trying to add an integer and a list together. This occurs when the value parameter passed to the incr method is a list, and the method attempts to add it to an integer.

Q: What is the solution to prevent the "TypeError: unsupported operand type(s) for +: 'int' and 'list'" error from occurring?

A: To prevent the error from occurring, we need to modify the incr method to handle the edge case where the value is a list. We can achieve this by using the sum function to calculate the total value of the list.

Q: How do I modify the incr method to handle the edge case where the value is a list?

A: Here's the modified incr method:

def incr(self, value):
    if isinstance(value, list):
        total_value = sum(value)
        self.details[key] = self.details.get(key, 0) + total_value
    else:
        self.details[key] = self.details.get(key, 0) + value

Q: What is the purpose of the isinstance function in the modified incr method?

A: The isinstance function is used to check if the value parameter is an instance of the list type. If it is, the method proceeds to calculate the total value of the list using the sum function.

Q: Can you provide an example of how the modified incr method handles the edge case where the value is a list?

A: Here's an example:

usage = Usage()
usage.incr([10, 20, 30])  # value is a list
print(usage.details)  # output: {'key': 60}

Q: What are some best practices for handling edge cases in the incr method?

A: Here are some best practices:

  • Always check the type of the value parameter using the isinstance function.
  • Use the sum function to calculate the total value of a list.
  • Handle the edge case where the value is a list by calculating the total value and adding it to the existing value.
  • Test the modified incr method thoroughly to ensure it handles edge cases correctly.

Q: Can you provide a summary of the key takeaways from this Q&A article?

A: Here's a summary:

  • The "TypeError: unsupported operand type(s) for +: 'int' and 'list'" error occurs when the incr method tries to add an integer and a list together.
  • To prevent the error from occurring, modify the incr method to handle the edge case where the value is a list.
  • Use the isinstance function to check the type of the value parameter.
  • Use the sum function to calculate the total value of a list.
  • Test the modified incr method thoroughly to ensure it handles edge cases correctly.