diff --git a/posthog/ai/prompts.py b/posthog/ai/prompts.py index 0e5ef38b..a3d4eba2 100644 --- a/posthog/ai/prompts.py +++ b/posthog/ai/prompts.py @@ -532,6 +532,16 @@ def _fetch_prompt_from_api( if response.status_code == 404: raise Exception(f"[PostHog Prompts] {prompt_title} not found") + if response.status_code == 401: + raise Exception( + f"[PostHog Prompts] Authentication failed for {prompt_reference}. " + "The key may be missing, expired, or the wrong type. Prompt fetches " + "require a personal API key (starts with 'phx_'); a project secret " + "key is not accepted. Pass this key as personal_api_key when you " + "construct Prompts directly, or as secret_key when you configure the " + "PostHog client." + ) + if response.status_code == 403: raise Exception( f"[PostHog Prompts] Access denied for {prompt_reference}. " diff --git a/posthog/test/ai/test_prompts.py b/posthog/test/ai/test_prompts.py index 15d4b911..774646d1 100644 --- a/posthog/test/ai/test_prompts.py +++ b/posthog/test/ai/test_prompts.py @@ -402,6 +402,22 @@ def test_handle_403_response(self, mock_get_session): 'Access denied for prompt "restricted-prompt"', str(context.exception) ) + @patch("posthog.ai.prompts._get_session") + def test_handle_401_response(self, mock_get_session): + """Should explain the likely auth causes on a 401 response.""" + mock_get = mock_get_session.return_value.get + mock_get.return_value = MockResponse(status_code=401, ok=False) + + posthog = self.create_mock_posthog() + prompts = Prompts(posthog) + + with self.assertRaises(Exception) as context: + prompts.get("restricted-prompt", with_metadata=False) + + message = str(context.exception) + self.assertIn('Authentication failed for prompt "restricted-prompt"', message) + self.assertIn("personal API key", message) + def test_throw_when_no_personal_api_key_configured(self): """Should throw when no personal_api_key is configured.""" posthog = self.create_mock_posthog(personal_api_key=None)