OpenHands / OpenHands/software-agent-sdk

AttributeError: 'PromptTokensDetailsWrapper' object has no attribute 'cache_creation_tokens' in Telemetry._cache_buckets

Open Beginner friendly
#5,134 2 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
1.1k
Forks
542
Avg merge
1d 19h
Merged PRs (30d)
137

Description

Summary

Telemetry._cache_buckets raises 'PromptTokensDetailsWrapper' object has no attribute 'cache_creation_tokens' on LLM responses that report cache reads but no cache creation tokens. This crashes token/cost accounting (and the LLM call) instead of degrading gracefully to cache_write = 0.

Root cause

In openhands/sdk/llm/utils/telemetry.py, _cache_buckets checks for cache-creation tokens like this:

cache_write = (
    details.cache_creation_tokens
    if "cache_creation_tokens" in details.model_fields_set
    else 0
)

details is a litellm PromptTokensDetailsWrapper. The membership check "cache_creation_tokens" in details.model_fields_set is not a reliable presence test:

  • The wrapper's __init__ does del self.cache_creation_tokens whenever the value is None (litellm types/utils.py), physically removing the attribute from __dict__.
  • The field name can still be present in model_fields_set after the attribute is deleted — either because it was passed explicitly as None, or (on newer litellm) because the wrapper's __setattr__ mirrors writes between cache_write_tokens and cache_creation_tokens, stamping both names into model_fields_set even when one is subsequently del'd.

So the if branch is taken, details.cache_creation_tokens is accessed, and the attribute is gone → AttributeError.

This is hit by any backend response whose prompt_tokens_details carries cached_tokens (cache read) but no cache-creation count — a very common case (OpenAI prompt caching, DeepSeek, Anthropic cache reads, etc.), and especially reproducible with newer litellm versions (e.g. 1.101.0).

Reproduction

In Agent Canvas (LLM profile/model configuration):

  1. Duplicate the glm-5.2 model.
  2. Change the duplicated glm-5.2 to point to minimax-m3.
  3. Save.
  4. Notice the error: 'PromptTokensDetailsWrapper' object has no attribute 'cache_creation_tokens'.

Programmatic reproduction:

from litellm.types.utils import Usage, PromptTokensDetailsWrapper
from openhands.sdk.llm.utils.telemetry import Telemetry

details = PromptTokensDetailsWrapper(cached_tokens=25, cache_creation_tokens=None)
usage = Usage(prompt_tokens=100, completion_tokens=5)
usage.prompt_tokens_details = details

print("in fields_set:", "cache_creation_tokens" in details.model_fields_set)  # True
print("hasattr:", hasattr(details, "cache_creation_tokens"))                  # False
Telemetry._cache_buckets(usage)  # raises AttributeError

Fix

Use getattr with a default instead of trusting model_fields_set:

cache_write = getattr(details, "cache_creation_tokens", None)
return int(details.cached_tokens or 0), int(cache_write or 0)

getattr(..., None) returns None when the attribute was deleted, and int(cache_write or 0) normalizes None/00. This is robust across litellm versions and keeps the Anthropic/OpenAI mirror semantics intact (the wrapper already keeps cache_creation_tokens and cache_write_tokens in sync).

A PR with the fix and a regression test is forthcoming.


This issue was created by an AI agent (OpenHands) on behalf of @juanmichelini.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in openhands/sdk/llm/utils/telemetry.py at Telemetry._cache_buckets and reproduce the failure with the PromptTokensDetailsWrapper example in the issue. Verify that responses with cache reads but no cache-creation attribute produce cache_write = 0 without raising, and add a regression test for this case.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend, observability
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.