Error handling layers
Errors in a reactive channel can come from two places — an action body, or an effect re-running later because of what the action wrote. Three layers catch them, from most specific to last-resort. All three run on the graph thread, since that’s where actions and effects run.
1. rescue_from on the channel
Plain ActionCable error handling, and the right place for expected, per-action failures — validation errors, missing records:
class TodosChannel < ApplicationCable::Channel
include Hibiki::Rails::Channel
rescue_from ActiveRecord::RecordNotFound do |e|
transmit({ error: "not found" })
end
end
The handlers apply exactly as in a stock channel; the only difference is that they run on the graph thread, because that’s where the action body runs. Whatever a handler doesn’t catch falls through to layer 3.
2. Hibiki.error_handler — effect errors
When an action’s write invalidates effects, those effects re-run after the action body — so an error raised inside an effect isn’t in the action’s stack and rescue_from alone may not be where you want to reason about it. The core gem provides an app-level hook for exactly this:
# e.g. in an initializer
Hibiki.error_handler = ->(error, effect) do
Rails.error.report(error, context: { effect: effect.inspect })
end
When set, effect errors raised during a flush are routed here instead of propagating. hibiki_rails deliberately does not set this for you — it’s app policy. Unset, effect errors propagate out of the flush and land in layer 3.
3. The graph worker’s per-job rescue
The safety net. Every job the graph’s worker thread runs — graph build, action, flush — is wrapped in a per-job rescue, so one bad action can’t take the whole graph down. Anything the first two layers didn’t handle is reported to Rails’ error reporter:
Rails.error.report(error, handled: true, source: "hibiki_rails")
Subscribe your error service to the reporter (most already are) and filter on source: "hibiki_rails" if you want these separated. To change the behavior for one channel, override build_graph_actor and pass your own handler:
def build_graph_actor
Hibiki::Rails::GraphActor.new(on_error: ->(e) { MyErrorService.notify(e) })
end