lostisland/faraday · error · Faraday::RackBuilder::StackLocked

can't modify middleware stack after making a request

Error message

can't modify middleware stack after making a request

What it means

Faraday::RackBuilder freezes its handler list via lock! before the first request is executed; locked? then returns true (the handlers array is frozen). Every mutation method (use, insert, insert_before, insert_after, swap, delete, and the Connection helpers request/response/adapter that delegate to them) first calls raise_if_locked, which raises StackLocked with 'can't modify middleware stack after making a request'. The design makes a connection's middleware stack immutable once it has served traffic, because the built app closure is already fixed.

Source

Thrown at lib/faraday/rack_builder.rb:220

    #     :uri        - Proxy Server URI
    #     :user       - Proxy server username
    #     :password   - Proxy server password
    # :ssl - Hash of options for configuring SSL requests.
    def build_env(connection, request)
      exclusive_url = connection.build_exclusive_url(
        request.path, request.params,
        request.options.params_encoder
      )

      Env.new(request.http_method, request.body, exclusive_url,
              request.options, request.headers, connection.ssl,
              connection.parallel_manager)
    end

    private

    def raise_if_locked
      raise StackLocked, LOCK_ERR if locked?
    end

    def raise_if_adapter(klass)
      return unless klass <= Faraday::Adapter

      raise 'Adapter should be set using the `adapter` method, not `use`'
    end

    def ensure_adapter!
      raise MISSING_ADAPTER_ERROR unless @adapter
    end

    def adapter_set?
      !@adapter.nil?
    end

    def use_symbol(mod, key, ...)
      use(mod.lookup_middleware(key), ...)

View on GitHub (pinned to b25b1b26cc)

Solutions

  1. Move all middleware configuration into the Faraday.new block (or before the first request) so the stack is complete before it locks.
  2. When you need a different stack after requests have run, build a new connection: conn = Faraday.new(url) { |b| ... } — connections are cheap to create.
  3. Express per-request variability through middleware options or per-request req.options/headers instead of editing the stack.
  4. In tests, either create a fresh connection per example or stub/reset via a new Stubs/adapter instead of mutating the shared one.

Example fix

# before
conn = Faraday.new('https://api.example.com')
conn.get('/health')            # first request locks the stack
conn.response :json           # => Faraday::RackBuilder::StackLocked

# after
conn = Faraday.new('https://api.example.com') do |b|
  b.response :json            # configure before any request
  b.adapter :net_http
end
conn.get('/health')
Defensive patterns

Strategy: validation

Validate before calling

raise 'stack locked; build a new connection' if conn.builder.locked?
conn.use MyMiddleware

Type guard

def stack_mutable?(conn)
  !conn.builder.locked?
end

Try / catch

begin
  conn.response :json
rescue Faraday::RackBuilder::StackLocked
  conn = Faraday.new(conn.url_prefix.to_s) { |b| b.response :json; b.adapter :net_http }
end

Prevention

When it happens

Trigger: Calling conn.use MyMiddleware, conn.response :json, conn.request :retry, or conn.builder.delete(...) after any request has already run through that connection; test fixtures that make a warm-up/sanity request and then register instrumentation middleware; connection objects stored in constants or memoized service objects that get 'upgraded' mid-lifecycle.

Common situations: Long-lived connections in service objects where configuration code runs lazily on first use and middleware registration happens after a boot-time health check request; test suites that swap middleware per example while sharing one connection; code that tries to add an auth middleware right after the first request revealed a 401.

Related errors


AI-assisted analysis of lostisland/faraday@b25b1b26cc (2026-08-21). Data as JSON: /api/errors/05ace38c199e4e3b. Report an issue: GitHub.