Skip to content

fix(ollama): record failed spans when API raises exceptions#4127

Open
Charan-place wants to merge 2 commits into
traceloop:mainfrom
Charan-place:fix/ollama-error-span-recording
Open

fix(ollama): record failed spans when API raises exceptions#4127
Charan-place wants to merge 2 commits into
traceloop:mainfrom
Charan-place:fix/ollama-error-span-recording

Conversation

@Charan-place
Copy link
Copy Markdown

@Charan-place Charan-place commented May 11, 2026

Problem

Fixes #412

When Client._request or AsyncClient._request raises an exception
(e.g. HTTP error, connection refused, model not found), the ollama
instrumentation:

  • Never set span.status to ERROR
  • Never called span.record_exception()
  • Never called span.end()

The span leaked open and errors were invisible in traces.

Fix

Wrapped the wrapped(*args, **kwargs) call in both _wrap and _awrap
with try/except:

try:
    response = wrapped(*args, **kwargs)
except Exception as e:
    span.set_status(Status(StatusCode.ERROR, str(e)))
    span.record_exception(e)
    span.end()
    raise


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

* **Bug Fixes**
  * Ollama instrumentation now marks spans as errored, records exception details, and ensures spans are ended when API requests fail (including streaming paths).

* **Tests**
  * Added tests confirming spans are marked with error status and contain exception details for synchronous and asynchronous Ollama operations.

[![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/traceloop/openllmetry/pull/4127)
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

When `Client._request` or `AsyncClient._request` raises, the span was
left without an error status and was never ended. Wrap both `_wrap` and
`_awrap` API calls in try/except so exceptions are captured via
`set_status(ERROR)`, `record_exception`, and `span.end()` before
re-raising.

Fixes traceloop#412

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@CLAassistant
Copy link
Copy Markdown

CLAassistant commented May 11, 2026

CLA assistant check
All committers have signed the CLA.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0189b996-5293-493c-9974-0479a9ae2a56

📥 Commits

Reviewing files that changed from the base of the PR and between 7062780 and 3b51e3b.

📒 Files selected for processing (1)
  • packages/opentelemetry-instrumentation-ollama/opentelemetry/instrumentation/ollama/__init__.py

📝 Walkthrough

Walkthrough

The pull request adds exception handling across synchronous and asynchronous Ollama instrumentation paths: wrappers and streaming accumulators now catch errors, set span StatusCode.ERROR with the exception message, record exception events, end spans, and re-raise. Tests assert these behaviors for sync chat, sync generate, and async chat.

Changes

Exception Handling and Validation

Layer / File(s) Summary
Synchronous Wrapper
packages/opentelemetry-instrumentation-ollama/opentelemetry/instrumentation/ollama/__init__.py
The _wrap function now catches exceptions from the client call, sets span status to StatusCode.ERROR with the exception message, records the exception event, ends the span, and re-raises.
Asynchronous Wrapper
packages/opentelemetry-instrumentation-ollama/opentelemetry/instrumentation/ollama/__init__.py
The _awrap function mirrors the sync behavior: catches exceptions, marks span ERROR, records the exception event, ends the span, and re-raises.
Synchronous Streaming Accumulation
packages/opentelemetry-instrumentation-ollama/opentelemetry/instrumentation/ollama/__init__.py
_accumulate_streaming_response wraps iteration/response handling in try/except/finally to mark spans ERROR, record exceptions, re-raise, and always end the span.
Asynchronous Streaming Accumulation
packages/opentelemetry-instrumentation-ollama/opentelemetry/instrumentation/ollama/__init__.py
_aaccumulate_streaming_response mirrors the sync streaming change with try/except/finally to mark ERROR, record exceptions, re-raise, and ensure the span is ended.
Exception Scenario Tests
packages/opentelemetry-instrumentation-ollama/tests/test_exceptions.py
Adds three pytest cases (sync chat, sync generate, async chat) that patch _request to raise, assert the call re-raises, and verify a single finished span is StatusCode.ERROR, contains the exception message in span.status.description, and includes an "exception" event.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped through wrappers, streaming too,

Caught the errors and gave them their due.
ERROR spans now glow and sing,
Exceptions logged — what a thing!
Tests nod kindly, "All is true."

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main fix: recording failed spans when the Ollama API raises exceptions, which matches the changeset.
Linked Issues check ✅ Passed The PR directly addresses issue #412 by implementing proper error handling to mark spans as failed and record exceptions when Ollama API requests fail.
Out of Scope Changes check ✅ Passed All changes are scoped to the Ollama instrumentation package and directly relate to implementing proper error handling for failed spans, with no extraneous modifications.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/opentelemetry-instrumentation-ollama/tests/test_exceptions.py (1)

11-98: ⚡ Quick win

Add regression tests for streaming failures after _request returns

These tests validate call-time exceptions, but not stream-consumption exceptions. Please add sync/async stream=True cases where the iterator raises mid-stream, then assert span is ended and marked StatusCode.ERROR with an exception event.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/opentelemetry-instrumentation-ollama/tests/test_exceptions.py`
around lines 11 - 98, Add two new tests mirroring the existing ones but using
stream=True to exercise streaming failures: for sync, patch
OllamaClient._request to return an iterator that yields one item then raises an
Exception("stream error"), call ollama.chat(..., stream=True) and iterate to
trigger the error; for async, patch OllamaAsyncClient._request to return an
async iterator that yields then raises and await iteration from
ollama.AsyncClient().chat(..., stream=True). In both tests instrument with
OllamaInstrumentor (instrument/uninstrument as in the other tests) and after the
exception assert exactly one finished span exists, that span.status.status_code
== StatusCode.ERROR, span.status.description contains the error message, and
that span.events contains an event with name "exception" to confirm span.end()
and exception recording.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@packages/opentelemetry-instrumentation-ollama/opentelemetry/instrumentation/ollama/__init__.py`:
- Around line 312-318: The try/except around the immediate call only handles
call-time exceptions and misses errors raised while consuming streaming
iterators; update the streaming-paths so that any exception raised during
iteration in _accumulate_streaming_response and _aaccumulate_streaming_response
is caught, call span.set_status(Status(StatusCode.ERROR, str(e))) and
span.record_exception(e), ensure span.end() is always called before re-raising
the exception, and likewise wrap the generator/async-generator returned for
stream=True so iteration failures trigger the same error recording and span
closure (refer to span, _accumulate_streaming_response,
_aaccumulate_streaming_response and the stream-returning wrapper functions).

---

Nitpick comments:
In `@packages/opentelemetry-instrumentation-ollama/tests/test_exceptions.py`:
- Around line 11-98: Add two new tests mirroring the existing ones but using
stream=True to exercise streaming failures: for sync, patch
OllamaClient._request to return an iterator that yields one item then raises an
Exception("stream error"), call ollama.chat(..., stream=True) and iterate to
trigger the error; for async, patch OllamaAsyncClient._request to return an
async iterator that yields then raises and await iteration from
ollama.AsyncClient().chat(..., stream=True). In both tests instrument with
OllamaInstrumentor (instrument/uninstrument as in the other tests) and after the
exception assert exactly one finished span exists, that span.status.status_code
== StatusCode.ERROR, span.status.description contains the error message, and
that span.events contains an event with name "exception" to confirm span.end()
and exception recording.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8fe7e748-d544-46e8-8892-de2364c58201

📥 Commits

Reviewing files that changed from the base of the PR and between 6d3e696 and 7062780.

📒 Files selected for processing (2)
  • packages/opentelemetry-instrumentation-ollama/opentelemetry/instrumentation/ollama/__init__.py
  • packages/opentelemetry-instrumentation-ollama/tests/test_exceptions.py

Streaming errors raised while consuming _accumulate_streaming_response
or _aaccumulate_streaming_response were not caught, leaving spans open
without an error status.

Wrap both generator loops in try/except/finally so that:
- iteration errors set span status to ERROR and record the exception
- span.end() is always called via finally (success and error paths)
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.

🐛 Bug Report: Errors are not logged

2 participants