{"record":{"id":"05ace38c199e4e3b","repo":"lostisland/faraday","slug":"can-t-modify-middleware-stack-after-making-a-reque","errorCode":null,"errorMessage":"can't modify middleware stack after making a request","messagePattern":"can't modify middleware stack after making a request","errorType":"exception","errorClass":"Faraday::RackBuilder::StackLocked","httpStatus":null,"severity":"error","filePath":"lib/faraday/rack_builder.rb","lineNumber":220,"sourceCode":"    #     :uri        - Proxy Server URI\n    #     :user       - Proxy server username\n    #     :password   - Proxy server password\n    # :ssl - Hash of options for configuring SSL requests.\n    def build_env(connection, request)\n      exclusive_url = connection.build_exclusive_url(\n        request.path, request.params,\n        request.options.params_encoder\n      )\n\n      Env.new(request.http_method, request.body, exclusive_url,\n              request.options, request.headers, connection.ssl,\n              connection.parallel_manager)\n    end\n\n    private\n\n    def raise_if_locked\n      raise StackLocked, LOCK_ERR if locked?\n    end\n\n    def raise_if_adapter(klass)\n      return unless klass <= Faraday::Adapter\n\n      raise 'Adapter should be set using the `adapter` method, not `use`'\n    end\n\n    def ensure_adapter!\n      raise MISSING_ADAPTER_ERROR unless @adapter\n    end\n\n    def adapter_set?\n      !@adapter.nil?\n    end\n\n    def use_symbol(mod, key, ...)\n      use(mod.lookup_middleware(key), ...)","sourceCodeStart":202,"sourceCodeEnd":238,"githubUrl":"https://github.com/lostisland/faraday/blob/b25b1b26ccef34b1460b0267115be238ca758087/lib/faraday/rack_builder.rb#L202-L238","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Move all middleware configuration into the Faraday.new block (or before the first request) so the stack is complete before it locks.","When you need a different stack after requests have run, build a new connection: conn = Faraday.new(url) { |b| ... } — connections are cheap to create.","Express per-request variability through middleware options or per-request req.options/headers instead of editing the stack.","In tests, either create a fresh connection per example or stub/reset via a new Stubs/adapter instead of mutating the shared one."],"exampleFix":"# before\nconn = Faraday.new('https://api.example.com')\nconn.get('/health')            # first request locks the stack\nconn.response :json           # => Faraday::RackBuilder::StackLocked\n\n# after\nconn = Faraday.new('https://api.example.com') do |b|\n  b.response :json            # configure before any request\n  b.adapter :net_http\nend\nconn.get('/health')","handlingStrategy":"validation","validationCode":"raise 'stack locked; build a new connection' if conn.builder.locked?\nconn.use MyMiddleware","typeGuard":"def stack_mutable?(conn)\n  !conn.builder.locked?\nend","tryCatchPattern":"begin\n  conn.response :json\nrescue Faraday::RackBuilder::StackLocked\n  conn = Faraday.new(conn.url_prefix.to_s) { |b| b.response :json; b.adapter :net_http }\nend","preventionTips":["Put all middleware setup in the Faraday.new block; treat the stack as immutable after creation.","Need a different stack later? Create a new connection — never edit a warmed one.","Model per-request variability as middleware options / per-request req options, not stack mutations.","In test suites, build a fresh connection per example instead of mutating a shared one."],"tags":["ruby","faraday","middleware","rack-builder","immutability","connection-lifecycle"],"backgroundTag":"config-locked-after-use","analyzedSha":"b25b1b26ccef34b1460b0267115be238ca758087","analyzedAt":"2026-08-21T19:43:20.220Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}