Skip to content

Faraday integration raises StackLocked when a RackBuilder is reused #3078

Description

@joshforbes

Issue Description

Sentry's Faraday integration calls builder.insert from every Faraday::Connection#initialize for non-Net::HTTP adapters.

Some clients reuse one Faraday::RackBuilder across multiple connections. After the first request locks that builder, constructing another connection causes Sentry to attempt a second insertion and raise Faraday::RackBuilder::StackLocked.

We encountered this with Ably Ruby, which reuses one builder for its primary and fallback connections. When the primary request fails, Ably constructs a fallback connection using the same builder. Sentry raises while that connection is being initialized, before the fallback request can be attempted. The exception escapes into the application and interrupts Ably's normal fallback handling.

Reproduction Steps

With sentry-ruby 7.0.0 and Faraday 1.10.6 installed, run:

require "faraday"
require "sentry-ruby"

Sentry.init do |config|
  config.dsn = nil
  config.enabled_patches = [:faraday]
end

stubs = Faraday::Adapter::Test::Stubs.new do |stub|
  stub.get("/") { [200, {}, "ok"] }
end

builder = Faraday::RackBuilder.new do |connection|
  connection.adapter :test, stubs
end

primary = Faraday.new("https://primary.example", builder: builder)
primary.get("/")

Faraday.new("https://fallback.example", builder: builder)

The test adapter keeps the reproduction network-free. Calling primary.get causes Faraday to lock the builder in the same way as a normal request.

Expected Behavior

Constructing another connection with a builder already instrumented by Sentry should succeed without attempting to modify that builder again.

Could Sentry make the Faraday middleware insertion idempotent for each builder? In this case, Sentry inserted its instrumentation before the builder was locked, so the next connection could detect that the same builder has already been instrumented and skip the second builder.insert.

Disabling the :faraday patch avoids the exception, but also removes outbound tracing for Faraday clients using non-Net::HTTP adapters.

Actual Behavior

The final Faraday.new raises:

Faraday::RackBuilder::StackLocked:
can't modify middleware stack after making a request

faraday/rack_builder.rb:221:in `raise_if_locked'
faraday/rack_builder.rb:121:in `insert'
sentry-ruby/lib/sentry/faraday.rb:20:in `initialize'

The same reproduction constructs the second connection successfully when Sentry's :faraday patch is disabled.

Ruby Version

4.0.2

SDK Version

7.0.0

Integration and Its Version

  • Faraday 1.10.6
  • Ably 1.2.0 in the original application
  • Typhoeus adapter in the original application

The minimal reproduction uses Faraday's test adapter.

Sentry Config

Sentry.init do |config|
  config.enabled_patches = [:faraday]
end

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions