Skip to content

fix(telemetry): skip signal handler registration in non-main threads#4649

Merged
greysonlalonde merged 4 commits intocrewAIInc:mainfrom
giulio-leone:fix/telemetry-signal-handler-non-main-thread
Mar 2, 2026
Merged

fix(telemetry): skip signal handler registration in non-main threads#4649
greysonlalonde merged 4 commits intocrewAIInc:mainfrom
giulio-leone:fix/telemetry-signal-handler-non-main-thread

Conversation

@giulio-leone
Copy link
Contributor

@giulio-leone giulio-leone commented Feb 28, 2026

Problem

When CrewAI is initialized from a non-main thread (e.g. Streamlit, Flask, Django, Jupyter notebooks), the telemetry module prints multiple noisy ValueError tracebacks:

Cannot register SIGTERM handler: not running in main thread
Traceback (most recent call last):
  File ".../crewai/telemetry/telemetry.py", line 214, in _register_signal_handler
    signal.signal(sig, handler)
ValueError: signal only works in main thread of the main interpreter

This is repeated for SIGTERM, SIGINT, SIGHUP, SIGTSTP, and SIGCONT — up to 5 tracebacks per initialization.

Root Cause

_register_shutdown_handlers() calls signal.signal() unconditionally. When running in a non-main thread, Python raises ValueError which is caught but logged with exc_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 a logger.debug() message. The atexit handler still registers (it works from any thread).

Test

Added test_no_signal_handler_traceback_in_non_main_thread that verifies:

  • No exceptions raised when initializing telemetry from a non-main thread
  • No ValueError tracebacks in stderr output

Fixes #4289


Note

Low Risk
Low risk: adds a simple main-thread guard around signal.signal registration and a regression test; behavior change is limited to telemetry shutdown signal handling when initialized off the main thread.

Overview
Prevents noisy ValueError tracebacks when Telemetry is initialized from a non-main thread by skipping all signal handler registration in _register_shutdown_handlers unless running on the main thread (while still registering the atexit shutdown hook).

Adds a regression test that initializes Telemetry in a background thread and asserts signal.signal is 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.

@giulio-leone
Copy link
Contributor Author

Friendly ping — CI is green and this is ready for review. Happy to address any feedback. Thanks!

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot AI review requested due to automatic review settings March 1, 2026 01:42
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 a debug-level log, while still registering the atexit handler.
  • Added a regression test test_no_signal_handler_traceback_in_non_main_thread to 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.

@giulio-leone giulio-leone force-pushed the fix/telemetry-signal-handler-non-main-thread branch 2 times, most recently from 9c0e79f to cf15c2d Compare March 1, 2026 02:15
@giulio-leone
Copy link
Contributor Author

Both review items addressed: comments removed, capsys replaced with mock signal.signal assertion. All 10 tests pass.

@giulio-leone
Copy link
Contributor Author

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?

giulio-leone added a commit to giulio-leone/crewAI that referenced this pull request Mar 1, 2026
@giulio-leone giulio-leone force-pushed the fix/telemetry-signal-handler-non-main-thread branch from 8aa8892 to 3e6d496 Compare March 1, 2026 04:21
@giulio-leone
Copy link
Contributor Author

@greysonlalonde Both requested changes have been addressed:

  1. Inline comments removed (commit 231f7c3) — only the debug log message remains
  2. capsys replaced with mock (commit 9c0e79f) — test now mocks signal.signal and asserts it is never called from a non-main thread

All CI checks are passing ✅. Ready for re-review when convenient.

@giulio-leone giulio-leone force-pushed the fix/telemetry-signal-handler-non-main-thread branch from e2af456 to 1204ae0 Compare March 1, 2026 06:27
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
@giulio-leone giulio-leone force-pushed the fix/telemetry-signal-handler-non-main-thread branch from 1fc2dfc to db07bfc Compare March 1, 2026 23:12
Copy link
Contributor

@greysonlalonde greysonlalonde left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@greysonlalonde greysonlalonde merged commit b76022c into crewAIInc:main Mar 2, 2026
42 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Noisy ValueError tracebacks when CrewAI initialized from non-main thread

3 participants