Skip to content

fix(rails): match excluded exceptions on subclasses and cause chain - #331

Merged
marandaneto merged 2 commits into
PostHog:mainfrom
mkushner:fix/rails-excluded-exceptions-chain
Nov 10, 2026
Merged

marandaneto merged 2 commits into
PostHog:mainfrom
mkushner:fix/rails-excluded-exceptions-chain

Conversation

@mkushner

@mkushner mkushner commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

💡 Motivation and Context

posthog-rails checks excluded_exceptions with an exact match on the outermost exception class name. However, two common cases are missing:

  • Wrapped exceptions. Sidekiq retries wrap the job error in Sidekiq::JobRetry::Handled with the real error as its cause. Listing Sidekiq::Limiter::OverLimit therefore excludes nothing, because the top-level exception class is the wrapper. E.g. in our production app this caused ~35k rate-limiter retries through in 6 hrs before I added a before_send filter that walks $exception_list.
  • Subclasses. Listing a base error class does not exclude its subclasses.

The sdk-specs "Ignored exception types" requirement covers this. Matching must consider every exception in the chain and SDK holding the live error object may match subtypes.

This PR brings excluded_exceptions in line with that, for the paths posthog-rails captures automatically:

  • Configuration#should_capture_exception? walks the cause chain, up to ExceptionCapture::MAX_CHAINED_EXCEPTIONS (the number of links SDK serializes into $exception_list). It excludes an exception when any link is an instance of a listed class or of a subclass.
  • ActiveJob captures auto_instrument_active_job now respect excluded_exceptions. Before they were the only automatic path that ignored it.
  • The excluded_exceptions YARD doc, the generator template and the example initializer document the matching rules.

Behaviour change
More events are excluded than before: subclasses of listed classes, and exceptions raised while handling a listed one. With the built-in defaults an error raised inside rescue ActiveRecord::RecordNotFound block is now dropped, because its cause is RecordNotFound. FYI sentry-ruby behaves the same way (inspect_exception_causes_for_exclusion is on by default). Among the default list the only subclass Rails ships is ActionController::MissingExactTemplate < UnknownFormat, which Rails already answers with 406. I marked the changeset minor because of this, but happy to change it if needed.

Remaining divergence from the spec
The spec also asks for the default list to be empty and for the rule to cover manual PostHog.capture_exception and $exception events sent through capture. This PR leaves both as they are: Rails apps rely on the default list, and applying it to manual captures would silently drop events people send on purpose.

Open question
If you'd rather keep the built-in defaults outermost-only, or add a switch like sentry-ruby inspect_exception_causes_for_exclusion - again, happy to change it. before_send already lets an app restore the old behaviour, so I didn't add a new option.

💚 How did you test it?

  • New specs cover subclass matching, an excluded class found in the cause chain, and ActiveJob respecting the list.
  • Ran the new specs against the unchanged code: all three fail there and pass with this change.
  • The full suite passes locally
  • Tested in our prod app patched configuration over the installed gem

📝 Checklist

  • All commits are signed (see Commit signing)
  • I reviewed the submitted code.
  • I added tests to verify the changes.
  • I updated the docs if needed.
  • No breaking change or entry added to the changelog.

If releasing new changes

  • Ran pnpm changeset to generate a changeset file

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

  • Code Autocomplete from Cursor IDE did some work (no clue if it counts or not as "agent assisted", but playing safe here).

@mkushner
mkushner requested a review from a team as a code owner November 5, 2026 14:51

@marandaneto marandaneto left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The code looks good; no qualifying findings. Verdict: correct.

Preserve retry configuration and exclusion documentation. Use the shared cause-chain filter for Active Job retries to keep exclusions within the serialized chain limit.
@marandaneto
marandaneto merged commit 9262fee into PostHog:main Nov 10, 2026
28 checks passed
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.

2 participants