[bug] Anthropic instrumentor: unguarded usage.input_tokens can blank out an entire streaming LLM span
buglanguage: pythoninstrumentation: anthropic
### Summary
`_get_token_counts` (recently extracted into `_utils.py` and now shared by the streaming and non-streaming paths) sums `usage.input_tokens` without the `or 0` guard applied to every other field:
```python
prompt_tokens = (
usage.input_tokens # <- no `or 0`
+ (usage.cache_creation_input_tokens or 0)
+ (usage.cache_read_input_tokens or 0)
)
```
If `usage.input_tokens` is `None`, `None + 0` raises `TypeError`.
### Why it matters more now
Both paths call the same helper, but they wrap it differently:
- **Non-streaming** — `_wrappers._get_llm_token_counts` is decorated with `@_stop_on_exception`, so the error is swallowed and the span quietly loses its token counts.
- **Streaming** — `_MessageExtractor.get_attributes()` has no such guard. The exception escapes into `_utils._finish_tracing`, which catches it and sets `attributes = None`. The exported LLM span then ends with **no** input messages, output messages, model name or token counts — an empty LLM span.
So one nullable field on a partial/delta-shaped `Usage` degrades a whole streaming span, not just its token attributes.
### Suggested fix
Guard `input_tokens` like the rest (`(usage.input_tokens or 0)`), and consider giving the streaming extractor the same `@_stop_on_exception`-style containment so a token-count failure can never blank out the rest of a span's attributes.
### Notes
Pre-existing behavior — not introduced by #3490's fix, which only moved the code. Found during a code review of that change.
0 条评论