fix(rails): ensure Rails.error.set_context is not lost#3024
Conversation
0b442b1 to
c29006f
Compare
| module Sentry | ||
| module Rails | ||
| module StructureSanitizer | ||
| def self.sanitize(value, &leaf) |
There was a problem hiding this comment.
why does this need to take a block
There was a problem hiding this comment.
@sl0thentr0py because ErrorReporterContext uses it for primitive-based filtering:
sanitized = Sentry::Rails::StructureSanitizer.sanitize(execution_context) do |leaf|
PRIMITIVE_CLASSES.any? { |klass| leaf.is_a?(klass) } ? leaf : leaf.class.name
endc29006f to
69d3b44
Compare
| end | ||
|
|
||
| { "rails.error" => sanitized } | ||
| end |
There was a problem hiding this comment.
Tags and hint context ignored
Medium Severity
ErrorReporterContext.contexts dumps the full ExecutionContext into rails.error, but unlike ErrorSubscriber it never lifts tags or hint out of that hash. For unhandled exceptions and ActiveJob failures, CaptureExceptions/ActiveJob usually win the capture race, so Rails.error.set_context(tags: ...) / hint: ... still never become real Sentry tags or hints.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 22f13b0. Configure here.
There was a problem hiding this comment.
That's material for a follow-up PR.
22f13b0 to
bb0629b
Compare
|
|
||
| sanitized = Sentry::Rails::StructureSanitizer.sanitize(execution_context) do |leaf| | ||
| PRIMITIVE_CLASSES.any? { |klass| leaf.is_a?(klass) } ? leaf : leaf.class.name | ||
| end |
There was a problem hiding this comment.
Timestamps reduced to class names
Medium Severity
PRIMITIVE_CLASSES keeps Symbol because Rails JSON can encode it, but common context values like Time, ActiveSupport::TimeWithZone, and Date are replaced with their class name. That drops useful Rails.error.set_context data on the CaptureExceptions and ActiveJob paths, while ErrorSubscriber still preserves those values.
Reviewed by Cursor Bugbot for commit bb0629b. Configure here.
| def self.sanitize_range(range, &leaf) | ||
| return range.to_s if range.begin.nil? || range.end.nil? | ||
|
|
||
| boundary = range.begin | ||
|
|
||
| if boundary.is_a?(::ActiveSupport::TimeWithZone) | ||
| range.to_s | ||
| else | ||
| range.map { |v| sanitize(v, &leaf) } | ||
| end |
There was a problem hiding this comment.
Bug: The sanitize_range method can raise a TypeError for ranges with mixed types (e.g., 1..Time.current), which crashes the exception capturing process because it's unhandled.
Severity: HIGH
Suggested Fix
Update the condition in sanitize_range to check if either range.begin or range.end is an ActiveSupport::TimeWithZone before attempting to iterate. This will correctly convert such ranges to strings instead of causing a TypeError.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.
Location: sentry-rails/lib/sentry/rails/structure_sanitizer.rb#L25-L34
Potential issue: The `sanitize_range` method was refactored to only check if
`range.begin` is an `ActiveSupport::TimeWithZone`. If an ActiveJob fails and its
arguments contain a range with a non-time start and a time end (e.g.,
`1..Time.current`), the sanitization logic will attempt to call `.map` on this
non-iterable range, raising a `TypeError`. Because the call to `sentry_context` within
`capture_exception` is not wrapped in an error handler, this `TypeError` will propagate,
preventing the original exception from being reported to Sentry and potentially crashing
the job worker.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 3 total unresolved issues (including 2 from previous reviews).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit d26b269. Configure here.
| { "rails.error" => sanitized } | ||
| rescue StandardError => e | ||
| Sentry.sdk_logger&.error("sentry-rails: failed to sanitize Rails.error execution context: #{e.class}: #{e.message}\n #{Array(e.backtrace).first(5).join("\n ")}") | ||
| {} |
There was a problem hiding this comment.
Rescue misses stack overflow failures
Medium Severity
contexts rescues only StandardError, but recursive sanitization of cyclic or deeply nested ExecutionContext values raises SystemStackError, which subclasses Exception. That escapes the handler, so capture_exception never runs and the middleware can surface the stack error instead of the original exception—contrary to the “still report when context can’t be sanitized” guarantee.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit d26b269. Configure here.


Use
ActiveSupport::ExecutionContextto maintain in the context what's set viaRails.error.set_context.Closes #2931