From cf811c19acbf0a9a5abb43f506f2878456925745 Mon Sep 17 00:00:00 2001 From: SUHAIL <114918598+suhailopensource@users.noreply.github.com> Date: Thu, 27 Aug 2026 14:06:16 +0530 Subject: [PATCH 1/2] fix(v10/core): Store child span timeout handle in _childSpanTimeoutID Backport of: #23429 --- packages/core/src/tracing/idleSpan.ts | 2 +- .../core/test/lib/tracing/idleSpan.test.ts | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) 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(); From 4489a5909223d9f0ee41a06323e9cbbc83390cae Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Thu, 27 Aug 2026 10:42:26 +0200 Subject: [PATCH 2/2] docs(v10/changelog): Credit @suhailopensource for #23429 backport Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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