fix(telemetry): skip signal handler registration in non-main threads#4649
Conversation
|
Friendly ping — CI is green and this is ready for review. Happy to address any feedback. Thanks! |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Free Tier Details
Your team is on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle for each member of your team.
To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
There was a problem hiding this comment.
Pull request overview
This PR fixes a noisy UX problem where CrewAI would print multiple ValueError tracebacks when telemetry was initialized from a non-main thread (e.g., Streamlit, Flask, Jupyter). The root cause was _register_shutdown_handlers() unconditionally calling signal.signal(), which Python only allows from the main thread.
Changes:
- Added a
threading.current_thread() is not threading.main_thread()guard in_register_shutdown_handlers()to skip signal registration in non-main threads with adebug-level log, while still registering theatexithandler. - Added a regression test
test_no_signal_handler_traceback_in_non_main_threadto verify the fix.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
lib/crewai/src/crewai/telemetry/telemetry.py |
Adds thread check before signal handler registration; returns early with a debug log if not on the main thread |
lib/crewai/tests/telemetry/test_telemetry.py |
Adds regression test verifying no exceptions and no signal.signal calls when telemetry is initialized from a non-main thread |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
9c0e79f to
cf15c2d
Compare
|
Both review items addressed: comments removed, capsys replaced with mock signal.signal assertion. All 10 tests pass. |
|
All feedback from @greysonlalonde addressed: removed inline comments (debug log is sufficient), replaced capsys with signal.signal mock + threading.Event. All CI checks pass. Could you re-review? |
8aa8892 to
3e6d496
Compare
|
@greysonlalonde Both requested changes have been addressed:
All CI checks are passing ✅. Ready for re-review when convenient. |
e2af456 to
1204ae0
Compare
When CrewAI is initialized from a non-main thread (e.g. Streamlit, Flask, Django, Jupyter), the telemetry module attempted to register signal handlers which only work in the main thread. This caused multiple noisy ValueError tracebacks to be printed to stderr, confusing users even though the errors were caught and non-fatal. Check `threading.current_thread() is not threading.main_thread()` before attempting signal registration, and skip silently with a debug-level log message instead of printing full tracebacks. Fixes crewAIInc#4289
…ry() construction The patch context now activates inside init_in_thread so the mock is guaranteed to be active before and during Telemetry.__init__, addressing the Copilot review feedback. Refs: crewAIInc#4289
…rtion Replace signal.signal-only mock with combined logger + signal mock. Assert logger.debug was called with the skip message and signal.signal was never invoked from the non-main thread. Refs: crewAIInc#4289
1fc2dfc to
db07bfc
Compare
Problem
When CrewAI is initialized from a non-main thread (e.g. Streamlit, Flask, Django, Jupyter notebooks), the telemetry module prints multiple noisy
ValueErrortracebacks:This is repeated for SIGTERM, SIGINT, SIGHUP, SIGTSTP, and SIGCONT — up to 5 tracebacks per initialization.
Root Cause
_register_shutdown_handlers()callssignal.signal()unconditionally. When running in a non-main thread, Python raisesValueErrorwhich is caught but logged withexc_info=e, printing the full traceback.Fix
Check
threading.current_thread() is not threading.main_thread()before attempting any signal registration. If not in the main thread, skip signal registration entirely with alogger.debug()message. Theatexithandler still registers (it works from any thread).Test
Added
test_no_signal_handler_traceback_in_non_main_threadthat verifies:ValueErrortracebacks in stderr outputFixes #4289
Note
Low Risk
Low risk: adds a simple main-thread guard around
signal.signalregistration and a regression test; behavior change is limited to telemetry shutdown signal handling when initialized off the main thread.Overview
Prevents noisy
ValueErrortracebacks whenTelemetryis initialized from a non-main thread by skipping all signal handler registration in_register_shutdown_handlersunless running on the main thread (while still registering theatexitshutdown hook).Adds a regression test that initializes
Telemetryin a background thread and assertssignal.signalis never called and a debug log message is emitted.Written by Cursor Bugbot for commit db07bfc. This will update automatically on new commits. Configure here.