We use Rails' native error reporter (Rails.error) as the single interface for all error and exception reporting. It replaces our previous Honeybadger-specific calls and routes through our RailsErrorSubscriber, which forwards to Rails.logger and the OTel pipeline.
| Method | When to use |
|---|---|
Rails.error.report |
Report a rescued exception without re-raising it |
Rails.error.handle |
Execute a block — rescues and reports on error, returns fallback |
Rails.error.set_context |
Attach ambient context to every error reported in this request/job |
Use when you have already rescued an exception and want to report it without re-raising.
rescue SomeError => e
Rails.error.report(e, handled: true, severity: :error, context: { order_id: order.id })
endParameters:
| Param | Values | Notes |
|---|---|---|
handled: |
true / false |
true = you rescued it; false = unhandled/unexpected |
severity: |
:error, :warning, :info |
Defaults to :error |
context: |
Hash |
Merged with any ambient context from set_context |
Severity guidance:
:error— server errors (5xx), unexpected failures:warning— non-fatal, unexpected but recoverable (e.g. a missing icon asset):info— expected client errors (4xx), known edge cases
# Server error — default severity
Rails.error.report(e, handled: true)
# Client error — downgrade severity
Rails.error.report(e, handled: true, severity: :info, context: { status: 404 })
# Non-fatal warning
Rails.error.report(RuntimeError.new("Missing icon: #{name}"), handled: true, severity: :warning)Use when you want to attempt an operation and silently continue if it fails.
Rails.error.handle(context: { product_id: @product.id }) do
ExternalService.sync(@product)
endOptionally provide a fallback value:
result = Rails.error.handle(fallback: -> { [] }) do
fetch_remote_items
endSet key/value pairs that are automatically included in every error reported during the current request or job. Call this once early in the lifecycle — don't pass duplicated keys into every report call.
Rails.error.set_context(account_id: Current.account.id, user_id: Current.user.id)Context accumulates — each call merges with the existing context for the current execution.
The ErrorContext concern sets user/session context on every request. Authorized::Account extends it with account and organization IDs after authorization resolves.
# app/controllers/concerns/error_context.rb
def set_error_context
Rails.error.set_context(user_id: Current.session_user&.id, session_id: Current.session&.id)
end
# app/controllers/concerns/authorized/account.rb
def set_error_context
super
Rails.error.set_context(account_id: Current.account&.id, organization_id: Current.organization&.id)
endIn controller rescue blocks, call Rails.error.report with handled: true:
rescue SomeError => exception
Rails.error.report(exception, handled: true, context: { request_params: request.filtered_parameters })
render json: { error: "Something went wrong" }, status: :internal_server_error
endSet job-specific context at the start of perform, then report errors in rescue blocks:
def perform(import_id)
Rails.error.set_context(import_id: import_id, job_class: self.class.name)
# ... work ...
rescue StandardError => e
Rails.error.report(e, handled: true, context: { import_id: import_id })
raise
endFor the API job layer, Api::Concerns::ErrorNotifiable handles severity mapping from HTTP status codes automatically — use notify_failure(exception, :internal_server_error).
Follow the same pattern — rescue, then report with relevant context:
rescue SomeError => exception
Rails.error.report(exception, handled: true, context: { connection_id: id })
endAvoid Rails.error.report outside of a rescue block. If you are not handling an exception, let it bubble up so Rails captures it as unhandled.
# ❌ Don't use Honeybadger directly
Honeybadger.notify(e)
Honeybadger.context(user_id: user.id)
# ❌ Don't report without handled:
Rails.error.report(e) # handled: is required — be explicit
# ❌ Don't swallow errors silently
rescue => e
nil # error lost entirely
# ❌ Don't set context inside every report call — set it once at the top
Rails.error.report(e, handled: true, context: { user_id: current_user.id }) # duplicates ambient contextRailsErrorSubscriber (app/errors/rails_error_subscriber.rb) is registered in config/initializers/rails_error_reporter.rb. It receives every call to Rails.error.report and Rails.error.handle and writes a tagged log line:
[ErrorReporter] [MyError] Something went wrong account_id=123 user_id=456
This log line flows through the OTel log pipeline and surfaces in Honeybadger via the log ingestion subscriber.
- Rails Error Reporting Guide
app/errors/rails_error_subscriber.rbconfig/initializers/rails_error_reporter.rbapp/controllers/concerns/error_context.rbapp/jobs/api/concerns/error_notifiable.rb