diff --git a/CHANGELOG.md b/CHANGELOG.md index 022ca53cb128..2491b1113b39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ Across all AI integrations (OpenAI, Anthropic, Google GenAI, LangChain, and LangGraph), the SDK no longer sends an event to Sentry for errors that the AI framework propagates to your code. Previously the instrumentation reported these as unhandled (`handled: false`) before your own error handling ran, so an error your code caught still showed up in Sentry as an unhandled crash. The span is still marked as errored and the error still propagates, so reporting is left to your application: if your code does not handle the error, it reaches Sentry's global error handlers and is captured as unhandled, just like any other uncaught error. Errors that a provider surfaces as data on an otherwise successful response (such as Anthropic error-shaped responses or Google GenAI blocked content) are still captured, since your code never sees them propagate. -Work in this release was contributed by @ryanrho-mercor and @lux-in-tenebris-lucet. Thank you for your contributions! +Work in this release was contributed by @ryanrho-mercor, @lux-in-tenebris-lucet, and @suhailopensource. Thank you for your contributions! ## 10.71.0 diff --git a/packages/core/src/tracing/idleSpan.ts b/packages/core/src/tracing/idleSpan.ts index e07c9f15c457..86105416dc26 100644 --- a/packages/core/src/tracing/idleSpan.ts +++ b/packages/core/src/tracing/idleSpan.ts @@ -245,7 +245,7 @@ export function startIdleSpan(startSpanOptions: StartSpanOptions, options: Parti */ function _restartChildSpanTimeout(endTimestamp?: number): void { _cancelChildSpanTimeout(); - _idleTimeoutID = setTimeout(() => { + _childSpanTimeoutID = setTimeout(() => { if (!_finished && _autoFinishAllowed) { _finishReason = FINISH_REASON_HEARTBEAT_FAILED; span.end(endTimestamp); diff --git a/packages/core/test/lib/tracing/idleSpan.test.ts b/packages/core/test/lib/tracing/idleSpan.test.ts index 9ef6c834d251..4e5f9eee434b 100644 --- a/packages/core/test/lib/tracing/idleSpan.test.ts +++ b/packages/core/test/lib/tracing/idleSpan.test.ts @@ -765,6 +765,29 @@ describe('startIdleSpan', () => { expect(spanToJSON(idleSpan).timestamp).toBeDefined(); }); + it('measures the idle timeout from the last child end, not from the auto-finish signal', () => { + const idleSpan = startIdleSpan({ name: 'idle span' }, { disableAutoFinish: true, finalTimeout: 99_999 }); + const idleSpanId = idleSpan.spanContext().spanId; + + const child = startInactiveSpan({ name: 'inner' }); + + vi.advanceTimersByTime(500); + getClient()!.emit('idleSpanEnableAutoFinish', idleSpan); + + vi.advanceTimersByTime(700); + child!.end(); + + vi.advanceTimersByTime(TRACING_DEFAULTS.idleTimeout - 199); + expect(spanToJSON(idleSpan).timestamp).toBeUndefined(); + + const lateChild = startInactiveSpan({ name: 'late' }); + expect(spanToJSON(lateChild!).parent_span_id).toBe(idleSpanId); + + lateChild!.end(); + vi.advanceTimersByTime(TRACING_DEFAULTS.idleTimeout); + expect(spanToJSON(idleSpan).timestamp).toBeDefined(); + }); + it('times out at final timeout if disableAutoFinish=true', () => { const idleSpan = startIdleSpan({ name: 'idle span' }, { disableAutoFinish: true }); expect(idleSpan).toBeDefined();