How to Configure Event Timeouts in Bubus: A Complete Guide
Bubus provides a hierarchical timeout system that lets you configure default limits on the BaseEvent model, override them per-event instance, or set specific limits per-handler when dispatching or awaiting results.
The browser-use/bubus event bus library implements a robust timeout mechanism to prevent runaway handlers from blocking your application. Whether you're building long-running background processors or latency-critical user interfaces, knowing how to configure event timeouts in Bubus ensures your system remains responsive and predictable.
Understanding the Timeout Architecture
Bubus implements timeouts at multiple layers, from the core data model to the execution service. The system respects the most specific timeout provided, falling back to broader defaults when specific values are omitted.
Default Event Timeout in BaseEvent
Every event in Bubus inherits from BaseEvent defined in bubus/models.py. The model declares an event_timeout field with a default value of 300 seconds (5 minutes):
# bubus/models.py L22-L24
class BaseEvent(BaseModel):
event_timeout: float | None = 300.0
This value serves as the global default for any event instance that does not specify its own timeout.
Per-Handler Timeout Overrides
When the event bus executes a handler, it creates an EventResult object that tracks the execution. According to the source code in bubus/service.py, the effective timeout for a specific handler is determined by:
# bubus/service.py L972-L973 and L1099-L1100
timeout = timeout or event.event_timeout
This means an explicit timeout argument passed to the dispatch or processing method takes precedence over the event's default timeout.
Configuring Timeouts at Different Layers
You can configure timeouts globally, per-instance, per-handler, or when awaiting results. Each approach targets different use cases within the Bubus framework.
Set a Global Default Timeout
To change the default for all events of a specific type, subclass BaseEvent and override the event_timeout field:
from bubus.models import BaseEvent
class MyEvent(BaseEvent):
event_timeout: float | None = 60.0 # 1 minute default for all MyEvent instances
This approach ensures consistency across your application for specific event types without manually passing timeouts during instantiation.
Configure Per-Instance Timeouts
For one-off events requiring different limits, pass the event_timeout parameter during construction:
from bubus.models import BaseEvent
# Create an event with a 5-second timeout
event = BaseEvent(
event_type='QuickTask',
event_timeout=5.0
)
bus.dispatch(event)
This overrides the class default for that specific instance only.
Override Timeouts Per-Handler
When dispatching events through low-level APIs like step or process_event, you can specify a handler timeout that overrides both the event default and instance values:
# Force a 2-second limit regardless of event.event_timeout
await bus.step(event, timeout=2.0)
This is useful when the same event type might be processed by different handlers with varying latency requirements.
Timeouts When Waiting for Events
The expect and wait_until_idle methods accept timeout parameters to prevent indefinite blocking:
# bubus/service.py L591-L617 (expect) and L817-L839 (wait_until_idle)
# Wait up to 3 seconds for a specific event type
user_evt = await bus.expect('UserActionEvent', timeout=3.0)
# Wait until bus is idle, but timeout after 10 seconds
await bus.wait_until_idle(timeout=10.0)
If the timeout expires, these methods raise asyncio.TimeoutError.
Timeouts When Awaiting Results
When retrieving results from specific handlers, pass the timeout to event_result:
# bubus/models.py L485-L486
result = await my_event.event_result(
handler_name='process_data',
timeout=4.5
)
This forwards the timeout directly to asyncio.wait_for internally.
What Happens When a Timeout Occurs
Understanding the timeout behavior helps you implement proper error handling and cleanup logic.
When a handler exceeds its allotted time, Bubus performs the following actions as implemented in bubus/service.py (lines 1184-1194):
- Task Cancellation: The handler's asyncio task is cancelled immediately.
- Error Recording: A
TimeoutErroris recorded on the associatedEventResultobject. - Child Event Cleanup: Any child events spawned by the timed-out handler are cancelled via
event.event_cancel_pending_child_processing. - Structured Logging: The system logs a timeout tree via
log_timeout_treeto help diagnose which handler exceeded its budget.
This comprehensive cleanup ensures that timeout cascades don't leave orphaned tasks or inconsistent state in your application.
Summary
- Bubus provides a hierarchical timeout system with defaults at the
BaseEventlevel (300 seconds), overridable per-instance, per-handler, and during wait operations. - Configure global defaults by subclassing
BaseEventand settingevent_timeout. - Override per-instance by passing
event_timeoutduring event construction. - Override per-handler by passing
timeouttobus.step()or similar low-level APIs. - Set wait timeouts using the
timeoutparameter inbus.expect()andbus.wait_until_idle(). - Await results with timeouts by passing
timeouttoevent.event_result(). - Timeout behavior includes task cancellation, error recording, child event cleanup, and structured logging via
log_timeout_treeinbubus/service.py.
Frequently Asked Questions
What is the default timeout for events in Bubus?
The default timeout is 300 seconds (5 minutes), defined in the event_timeout field of the BaseEvent model in bubus/models.py (lines 22-24). You can override this default globally by subclassing BaseEvent or per-instance when creating specific events.
How do I set a different timeout for a specific event handler?
You can override the timeout for a specific handler dispatch by passing the timeout argument to low-level methods like bus.step(). According to the implementation in bubus/service.py (lines 972-973 and 1099-1100), the explicit timeout parameter takes precedence over the event's event_timeout attribute.
What exception is raised when an event times out in Bubus?
When waiting for events using bus.expect() or bus.wait_until_idle(), Bubus raises asyncio.TimeoutError if the specified timeout expires. For handler execution timeouts, the system records a TimeoutError on the EventResult object and cancels the handler task, as implemented in bubus/service.py (lines 1184-1194).
Does Bubus clean up child events when a parent handler times out?
Yes. When a handler exceeds its timeout budget, Bubus automatically cancels any child events that were spawned during the handler's execution. This cleanup is performed via event.event_cancel_pending_child_processing as part of the timeout handling logic in bubus/service.py, ensuring that timeout cascades don't leave orphaned tasks running.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →