Skip to content

Commit eb19c7f

Browse files
fix: prevent PostHog shutdown errors during interpreter shutdown
- Add interpreter shutdown detection using sys.is_finalizing() and thread tests - Skip PostHog operations when interpreter is shutting down to prevent: - cannot schedule new futures after interpreter shutdown errors - can''t register atexit after shutdown errors - Event loop and threading errors during shutdown - Enhanced error handling with specific shutdown error message filtering - Apply shutdown prevention to flush, thread cleanup, and shutdown operations - Maintain full backward compatibility with existing API - Add comprehensive debug logging for shutdown scenarios Fixes issue #1011 where PostHog telemetry was causing shutdown errors when force_shutdown_telemetry() was called during interpreter shutdown. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-authored-by: Mervin Praison <MervinPraison@users.noreply.github.com>
1 parent d8d9f46 commit eb19c7f

File tree

1 file changed

+104
-8
lines changed

1 file changed

+104
-8
lines changed

src/praisonai-agents/praisonaiagents/telemetry/telemetry.py

Lines changed: 104 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,12 @@ def shutdown(self):
347347
posthog_client = getattr(self, '_posthog', None)
348348
if posthog_client:
349349
try:
350+
# Check if Python interpreter is shutting down
351+
if self._is_interpreter_shutting_down():
352+
self.logger.debug("Interpreter shutting down, skipping PostHog operations")
353+
self._posthog = None
354+
return
355+
350356
# Use a timeout-based flush to prevent hanging
351357
import threading
352358
import time
@@ -368,27 +374,90 @@ def shutdown(self):
368374
# Cleanup PostHog threads safely
369375
self._shutdown_posthog_threads(posthog_client)
370376

371-
# Standard shutdown
372-
posthog_client.shutdown()
377+
# Standard shutdown - with interpreter shutdown check
378+
if not self._is_interpreter_shutting_down():
379+
posthog_client.shutdown()
380+
else:
381+
self.logger.debug("Skipping PostHog shutdown call due to interpreter shutdown")
373382

374383
except Exception as e:
375-
# Log the error but don't fail shutdown
376-
self.logger.error(f"Error during PostHog shutdown: {e}")
384+
# Handle specific shutdown-related errors gracefully
385+
error_msg = str(e).lower()
386+
if any(phrase in error_msg for phrase in [
387+
'cannot schedule new futures',
388+
'interpreter shutdown',
389+
'atexit after shutdown',
390+
'event loop closed',
391+
'runtime is shutting down'
392+
]):
393+
self.logger.debug(f"PostHog shutdown prevented due to interpreter shutdown: {e}")
394+
else:
395+
self.logger.error(f"Error during PostHog shutdown: {e}")
377396
finally:
378397
self._posthog = None
379398

399+
def _is_interpreter_shutting_down(self) -> bool:
400+
"""
401+
Check if the Python interpreter is shutting down.
402+
403+
Returns:
404+
True if interpreter is shutting down, False otherwise
405+
"""
406+
try:
407+
import sys
408+
import threading
409+
410+
# Check if the interpreter is in shutdown mode
411+
if hasattr(sys, 'is_finalizing') and sys.is_finalizing():
412+
return True
413+
414+
# Check if we can create new threads (fails during shutdown)
415+
try:
416+
import threading
417+
test_thread = threading.Thread(target=lambda: None)
418+
test_thread.daemon = True
419+
test_thread.start()
420+
test_thread.join(timeout=0.001)
421+
return False
422+
except (RuntimeError, threading.ThreadError):
423+
return True
424+
425+
except Exception:
426+
# If we can't determine state, assume we're shutting down to be safe
427+
return True
428+
380429
def _safe_flush_posthog(self, posthog_client):
381430
"""Safely flush PostHog data with error handling."""
382431
try:
432+
# Skip flush if interpreter is shutting down
433+
if self._is_interpreter_shutting_down():
434+
self.logger.debug("Skipping PostHog flush due to interpreter shutdown")
435+
return False
436+
383437
posthog_client.flush()
384438
return True
385439
except Exception as e:
386-
self.logger.debug(f"PostHog flush error: {e}")
440+
error_msg = str(e).lower()
441+
if any(phrase in error_msg for phrase in [
442+
'cannot schedule new futures',
443+
'interpreter shutdown',
444+
'atexit after shutdown',
445+
'event loop closed',
446+
'runtime is shutting down'
447+
]):
448+
self.logger.debug(f"PostHog flush prevented due to interpreter shutdown: {e}")
449+
else:
450+
self.logger.debug(f"PostHog flush error: {e}")
387451
return False
388452

389453
def _shutdown_posthog_threads(self, posthog_client):
390454
"""Safely shutdown PostHog background threads."""
391455
try:
456+
# Skip thread cleanup if interpreter is shutting down
457+
if self._is_interpreter_shutting_down():
458+
self.logger.debug("Skipping PostHog thread cleanup due to interpreter shutdown")
459+
return
460+
392461
# Access thread pool safely (fix double shutdown issue)
393462
thread_pool = getattr(posthog_client, '_thread_pool', None)
394463
if thread_pool:
@@ -400,7 +469,16 @@ def _shutdown_posthog_threads(self, posthog_client):
400469
import time
401470
time.sleep(0.5)
402471
except Exception as e:
403-
self.logger.debug(f"Thread pool shutdown error: {e}")
472+
error_msg = str(e).lower()
473+
if any(phrase in error_msg for phrase in [
474+
'cannot schedule new futures',
475+
'interpreter shutdown',
476+
'atexit after shutdown',
477+
'event loop closed'
478+
]):
479+
self.logger.debug(f"Thread pool shutdown prevented due to interpreter shutdown: {e}")
480+
else:
481+
self.logger.debug(f"Thread pool shutdown error: {e}")
404482

405483
# Clean up consumer
406484
consumer = getattr(posthog_client, '_consumer', None)
@@ -411,10 +489,28 @@ def _shutdown_posthog_threads(self, posthog_client):
411489
if hasattr(consumer, 'shutdown'):
412490
consumer.shutdown()
413491
except Exception as e:
414-
self.logger.debug(f"Consumer shutdown error: {e}")
492+
error_msg = str(e).lower()
493+
if any(phrase in error_msg for phrase in [
494+
'cannot schedule new futures',
495+
'interpreter shutdown',
496+
'atexit after shutdown',
497+
'event loop closed'
498+
]):
499+
self.logger.debug(f"Consumer shutdown prevented due to interpreter shutdown: {e}")
500+
else:
501+
self.logger.debug(f"Consumer shutdown error: {e}")
415502

416503
except Exception as e:
417-
self.logger.debug(f"Error during PostHog thread cleanup: {e}")
504+
error_msg = str(e).lower()
505+
if any(phrase in error_msg for phrase in [
506+
'cannot schedule new futures',
507+
'interpreter shutdown',
508+
'atexit after shutdown',
509+
'event loop closed'
510+
]):
511+
self.logger.debug(f"PostHog thread cleanup prevented due to interpreter shutdown: {e}")
512+
else:
513+
self.logger.debug(f"Error during PostHog thread cleanup: {e}")
418514

419515

420516
# Global telemetry instance

0 commit comments

Comments
 (0)