From fda06d88c20ca5f3a7c00a354913062764e27306 Mon Sep 17 00:00:00 2001 From: Andrew Maguire Date: Thu, 9 Apr 2026 20:57:00 +0100 Subject: [PATCH 1/8] fix: graceful fallback in claude_agent_sdk query wrapper when PostHog not configured MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When PostHog is not configured (no API key), the query() convenience function now catches the initialization error and falls back to the plain claude_agent_sdk.query() with a warning log instead of crashing. The wrapper should be fail-safe — instrumentation is additive and must never break the underlying SDK call. --- posthog/ai/claude_agent_sdk/__init__.py | 25 +++++++++++++------ .../ai/claude_agent_sdk/test_processor.py | 19 ++++++++++++++ 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/posthog/ai/claude_agent_sdk/__init__.py b/posthog/ai/claude_agent_sdk/__init__.py index 5fe1828d..88c7b731 100644 --- a/posthog/ai/claude_agent_sdk/__init__.py +++ b/posthog/ai/claude_agent_sdk/__init__.py @@ -1,5 +1,6 @@ from __future__ import annotations +import logging from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Union if TYPE_CHECKING: @@ -17,6 +18,8 @@ from posthog.ai.claude_agent_sdk.client import PostHogClaudeSDKClient from posthog.ai.claude_agent_sdk.processor import PostHogClaudeAgentProcessor +log = logging.getLogger("posthog") + __all__ = [ "PostHogClaudeAgentProcessor", "PostHogClaudeSDKClient", @@ -113,13 +116,21 @@ async def query( print(message) ``` """ - processor = PostHogClaudeAgentProcessor( - client=posthog_client, - distinct_id=posthog_distinct_id, - privacy_mode=posthog_privacy_mode, - groups=posthog_groups, - properties={}, - ) + from claude_agent_sdk import query as original_query + + try: + processor = PostHogClaudeAgentProcessor( + client=posthog_client, + distinct_id=posthog_distinct_id, + privacy_mode=posthog_privacy_mode, + groups=posthog_groups, + properties={}, + ) + except Exception as e: + log.warning("PostHog instrumentation disabled: %s — falling back to plain claude_agent_sdk.query()", e) + async for message in original_query(prompt=prompt, options=options, transport=transport): + yield message + return async for message in processor.query( prompt=prompt, diff --git a/posthog/test/ai/claude_agent_sdk/test_processor.py b/posthog/test/ai/claude_agent_sdk/test_processor.py index f36283c0..519d8c11 100644 --- a/posthog/test/ai/claude_agent_sdk/test_processor.py +++ b/posthog/test/ai/claude_agent_sdk/test_processor.py @@ -19,6 +19,7 @@ from posthog.ai.claude_agent_sdk import ( PostHogClaudeAgentProcessor, instrument, + query as posthog_query, ) CLAUDE_AGENT_SDK_AVAILABLE = True @@ -580,3 +581,21 @@ async def fake_query_capture(**kwargs): pass assert captured_options.get("options").include_partial_messages is True + + +class TestQueryGracefulFallback: + @pytest.mark.asyncio + async def test_falls_back_to_original_query_when_posthog_not_configured(self): + result_msg = _make_result() + messages_from_sdk = [result_msg] + + with ( + patch("posthog.ai.claude_agent_sdk.PostHogClaudeAgentProcessor", side_effect=ValueError("API key is required")), + patch("claude_agent_sdk.query", side_effect=lambda **kwargs: _fake_query(messages_from_sdk)), + ): + collected = [] + async for msg in posthog_query(prompt="Hello", options=ClaudeAgentOptions()): + collected.append(msg) + + assert len(collected) == 1 + assert collected[0] is result_msg From 480bed9877ada123972187d7ecd519b80ebd77be Mon Sep 17 00:00:00 2001 From: Andrew Maguire Date: Thu, 9 Apr 2026 20:58:26 +0100 Subject: [PATCH 2/8] chore: bump version to 7.10.4 --- posthog/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/posthog/version.py b/posthog/version.py index d09815e0..1361b50c 100644 --- a/posthog/version.py +++ b/posthog/version.py @@ -1 +1 @@ -VERSION = "7.10.3" +VERSION = "7.10.4" From 00cff5a9d30e9b741bfb403f5c3325748d545965 Mon Sep 17 00:00:00 2001 From: Andrew Maguire Date: Thu, 9 Apr 2026 20:58:45 +0100 Subject: [PATCH 3/8] Revert "chore: bump version to 7.10.4" This reverts commit 480bed9877ada123972187d7ecd519b80ebd77be. --- posthog/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/posthog/version.py b/posthog/version.py index 1361b50c..d09815e0 100644 --- a/posthog/version.py +++ b/posthog/version.py @@ -1 +1 @@ -VERSION = "7.10.4" +VERSION = "7.10.3" From 9bba2e7e9424ad2b2ba99253e51d320574ca5fa0 Mon Sep 17 00:00:00 2001 From: Andrew Maguire Date: Thu, 9 Apr 2026 21:00:10 +0100 Subject: [PATCH 4/8] chore: add sampo changeset for claude_agent_sdk graceful fallback --- .sampo/changesets/claude-agent-sdk-graceful-fallback.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .sampo/changesets/claude-agent-sdk-graceful-fallback.md diff --git a/.sampo/changesets/claude-agent-sdk-graceful-fallback.md b/.sampo/changesets/claude-agent-sdk-graceful-fallback.md new file mode 100644 index 00000000..4d78b58a --- /dev/null +++ b/.sampo/changesets/claude-agent-sdk-graceful-fallback.md @@ -0,0 +1,5 @@ +--- +pypi/posthog: patch +--- + +fix: graceful fallback in claude_agent_sdk query wrapper when PostHog is not configured From e6565c7a146fba660a7a4b97097d047b43a1c0ea Mon Sep 17 00:00:00 2001 From: Andrew Maguire Date: Thu, 9 Apr 2026 21:01:33 +0100 Subject: [PATCH 5/8] fix: narrow exception catch and fix test helper name Address review feedback: - Narrow except clause to (ValueError, RuntimeError) to avoid swallowing real bugs - Fix test to use _make_result_message() (the actual helper name) --- posthog/ai/claude_agent_sdk/__init__.py | 4 +++- posthog/test/ai/claude_agent_sdk/test_processor.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/posthog/ai/claude_agent_sdk/__init__.py b/posthog/ai/claude_agent_sdk/__init__.py index 88c7b731..cf5e106b 100644 --- a/posthog/ai/claude_agent_sdk/__init__.py +++ b/posthog/ai/claude_agent_sdk/__init__.py @@ -126,7 +126,9 @@ async def query( groups=posthog_groups, properties={}, ) - except Exception as e: + except (ValueError, RuntimeError) as e: + # PostHog is not configured (e.g. missing API key); fall back to the + # plain SDK so callers are never broken by missing instrumentation. log.warning("PostHog instrumentation disabled: %s — falling back to plain claude_agent_sdk.query()", e) async for message in original_query(prompt=prompt, options=options, transport=transport): yield message diff --git a/posthog/test/ai/claude_agent_sdk/test_processor.py b/posthog/test/ai/claude_agent_sdk/test_processor.py index 519d8c11..41ddedf0 100644 --- a/posthog/test/ai/claude_agent_sdk/test_processor.py +++ b/posthog/test/ai/claude_agent_sdk/test_processor.py @@ -586,7 +586,7 @@ async def fake_query_capture(**kwargs): class TestQueryGracefulFallback: @pytest.mark.asyncio async def test_falls_back_to_original_query_when_posthog_not_configured(self): - result_msg = _make_result() + result_msg = _make_result_message() messages_from_sdk = [result_msg] with ( From f53399b4004182adbf199004886903fcaa05438e Mon Sep 17 00:00:00 2001 From: Andrew Maguire Date: Thu, 9 Apr 2026 21:05:18 +0100 Subject: [PATCH 6/8] style: ruff format --- posthog/ai/claude_agent_sdk/__init__.py | 9 +++++++-- posthog/test/ai/claude_agent_sdk/test_processor.py | 14 +++++++++++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/posthog/ai/claude_agent_sdk/__init__.py b/posthog/ai/claude_agent_sdk/__init__.py index cf5e106b..b8d251a2 100644 --- a/posthog/ai/claude_agent_sdk/__init__.py +++ b/posthog/ai/claude_agent_sdk/__init__.py @@ -129,8 +129,13 @@ async def query( except (ValueError, RuntimeError) as e: # PostHog is not configured (e.g. missing API key); fall back to the # plain SDK so callers are never broken by missing instrumentation. - log.warning("PostHog instrumentation disabled: %s — falling back to plain claude_agent_sdk.query()", e) - async for message in original_query(prompt=prompt, options=options, transport=transport): + log.warning( + "PostHog instrumentation disabled: %s — falling back to plain claude_agent_sdk.query()", + e, + ) + async for message in original_query( + prompt=prompt, options=options, transport=transport + ): yield message return diff --git a/posthog/test/ai/claude_agent_sdk/test_processor.py b/posthog/test/ai/claude_agent_sdk/test_processor.py index 41ddedf0..3a797bd5 100644 --- a/posthog/test/ai/claude_agent_sdk/test_processor.py +++ b/posthog/test/ai/claude_agent_sdk/test_processor.py @@ -590,11 +590,19 @@ async def test_falls_back_to_original_query_when_posthog_not_configured(self): messages_from_sdk = [result_msg] with ( - patch("posthog.ai.claude_agent_sdk.PostHogClaudeAgentProcessor", side_effect=ValueError("API key is required")), - patch("claude_agent_sdk.query", side_effect=lambda **kwargs: _fake_query(messages_from_sdk)), + patch( + "posthog.ai.claude_agent_sdk.PostHogClaudeAgentProcessor", + side_effect=ValueError("API key is required"), + ), + patch( + "claude_agent_sdk.query", + side_effect=lambda **kwargs: _fake_query(messages_from_sdk), + ), ): collected = [] - async for msg in posthog_query(prompt="Hello", options=ClaudeAgentOptions()): + async for msg in posthog_query( + prompt="Hello", options=ClaudeAgentOptions() + ): collected.append(msg) assert len(collected) == 1 From 40641cb162b8c39e8ce32a933d05396862310824 Mon Sep 17 00:00:00 2001 From: Andrew Maguire Date: Thu, 9 Apr 2026 21:09:22 +0100 Subject: [PATCH 7/8] fix: narrow fallback to ValueError only --- posthog/ai/claude_agent_sdk/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/posthog/ai/claude_agent_sdk/__init__.py b/posthog/ai/claude_agent_sdk/__init__.py index b8d251a2..170a08c0 100644 --- a/posthog/ai/claude_agent_sdk/__init__.py +++ b/posthog/ai/claude_agent_sdk/__init__.py @@ -126,8 +126,8 @@ async def query( groups=posthog_groups, properties={}, ) - except (ValueError, RuntimeError) as e: - # PostHog is not configured (e.g. missing API key); fall back to the + except ValueError as e: + # PostHog is not configured (missing API key); fall back to the # plain SDK so callers are never broken by missing instrumentation. log.warning( "PostHog instrumentation disabled: %s — falling back to plain claude_agent_sdk.query()", From 2294d7ccd3edea72042ed05e882f2b4c23e3856e Mon Sep 17 00:00:00 2001 From: Andrew Maguire Date: Thu, 9 Apr 2026 21:10:20 +0100 Subject: [PATCH 8/8] test: verify non-config errors propagate through query wrapper --- posthog/test/ai/claude_agent_sdk/test_processor.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/posthog/test/ai/claude_agent_sdk/test_processor.py b/posthog/test/ai/claude_agent_sdk/test_processor.py index 3a797bd5..74bfc1e0 100644 --- a/posthog/test/ai/claude_agent_sdk/test_processor.py +++ b/posthog/test/ai/claude_agent_sdk/test_processor.py @@ -607,3 +607,15 @@ async def test_falls_back_to_original_query_when_posthog_not_configured(self): assert len(collected) == 1 assert collected[0] is result_msg + + @pytest.mark.asyncio + async def test_non_config_errors_propagate(self): + with patch( + "posthog.ai.claude_agent_sdk.PostHogClaudeAgentProcessor", + side_effect=RuntimeError("unexpected init bug"), + ): + with pytest.raises(RuntimeError, match="unexpected init bug"): + async for _ in posthog_query( + prompt="Hello", options=ClaudeAgentOptions() + ): + pass