Skip to content

fix: isolate event handler errors per spec 5.2.5#600

Closed
mujiaho wants to merge 1 commit into
open-feature:mainfrom
mujiaho:fix/issue-596-handler-error-isolation
Closed

fix: isolate event handler errors per spec 5.2.5#600
mujiaho wants to merge 1 commit into
open-feature:mainfrom
mujiaho:fix/issue-596-handler-error-isolation

Conversation

@mujiaho
Copy link
Copy Markdown

@mujiaho mujiaho commented May 27, 2026

What

Wrap handler invocations in try/except to isolate errors per OpenFeature spec 5.2.5.

If a handler function terminates abnormally, other handler functions MUST run.

Changes

  • run_client_handlers() — wrap handler(details) in try/except
  • run_global_handlers() — same
  • _run_immediate_handler() — same
  • All exceptions logged via logger.warning() with handler name, event type, and full traceback (exc_info=True)

Testing

  • Syntax check passes
  • Logic verified: raising handler does not prevent subsequent handlers

Closes #596

Wrap handler invocations in try/except within run_client_handlers,
run_global_handlers, and _run_immediate_handler so that a failing
handler does not prevent subsequent handlers from executing.

Log handler name, event type, and full traceback via logger.warning
with exc_info=True for debugging visibility.

Closes open-feature#596
@mujiaho mujiaho requested review from a team as code owners May 27, 2026 19:34
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces exception handling and logging when executing client, global, and immediate event handlers, preventing unhandled exceptions in user-defined callbacks from disrupting the execution flow. The feedback suggests a critical improvement: avoiding the execution of user-defined callbacks while holding locks (_client_lock and _global_lock) to prevent potential deadlocks and performance bottlenecks. Instead, the handler lists should be copied and executed outside the lock context. Additionally, the reviewer recommends using __qualname__ over __name__ for more descriptive logging of class methods and nested functions.

Comment on lines 35 to +44
for handler in _client_handlers[client][event]:
handler(details)
try:
handler(details)
except Exception:
logger.warning(
"Error in client handler %s for event %s",
getattr(handler, "__name__", repr(handler)),
event,
exc_info=True,
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Executing user-defined callbacks/handlers while holding a lock (_client_lock) is a known anti-pattern. If a handler blocks, performs a slow operation, or attempts to acquire another lock (or re-acquire this lock from another thread), it can lead to deadlocks or severely degrade performance by blocking other threads.

To prevent this, we should copy the list of handlers while holding the lock, and then iterate and execute them outside of the lock context.

Additionally, using __qualname__ instead of __name__ provides a more descriptive name for class methods or nested functions during logging.

Suggested change
for handler in _client_handlers[client][event]:
handler(details)
try:
handler(details)
except Exception:
logger.warning(
"Error in client handler %s for event %s",
getattr(handler, "__name__", repr(handler)),
event,
exc_info=True,
)
handlers = list(_client_handlers[client][event])
for handler in handlers:
try:
handler(details)
except Exception:
logger.warning(
"Error in client handler %s for event %s",
getattr(handler, "__qualname__", getattr(handler, "__name__", repr(handler))),
event,
exc_info=True,
)

Comment on lines 49 to +58
for handler in _global_handlers[event]:
handler(details)
try:
handler(details)
except Exception:
logger.warning(
"Error in global handler %s for event %s",
getattr(handler, "__name__", repr(handler)),
event,
exc_info=True,
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Similarly to run_client_handlers, executing user-defined global handlers while holding _global_lock can lead to deadlocks or performance issues if a handler blocks or performs slow operations.

We should copy the list of global handlers while holding the lock, and then execute them outside of the lock context. We can also use __qualname__ for better logging details.

Suggested change
for handler in _global_handlers[event]:
handler(details)
try:
handler(details)
except Exception:
logger.warning(
"Error in global handler %s for event %s",
getattr(handler, "__name__", repr(handler)),
event,
exc_info=True,
)
handlers = list(_global_handlers[event])
for handler in handlers:
try:
handler(details)
except Exception:
logger.warning(
"Error in global handler %s for event %s",
getattr(handler, "__qualname__", getattr(handler, "__name__", repr(handler))),
event,
exc_info=True,
)

Comment on lines +123 to +128
logger.warning(
"Error in immediate handler %s for event %s",
getattr(handler, "__name__", repr(handler)),
event,
exc_info=True,
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

For consistency and more descriptive logging (especially for class methods or nested functions), use __qualname__ if available.

Suggested change
logger.warning(
"Error in immediate handler %s for event %s",
getattr(handler, "__name__", repr(handler)),
event,
exc_info=True,
)
logger.warning(
"Error in immediate handler %s for event %s",
getattr(handler, "__qualname__", getattr(handler, "__name__", repr(handler))),
event,
exc_info=True,
)

@toddbaert
Copy link
Copy Markdown
Member

Closing as a duplicate of #599, which addresses the full scope of #596 (both error isolation and async dispatch). Thanks for the contribution.

@toddbaert toddbaert closed this May 28, 2026
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.

Event handler error isolation and async execution

2 participants