http: limit connected HTTPRemoteClients #35730

pull pinheadmz wants to merge 5 commits into bitcoin:master from pinheadmz:http-client-limit changing 7 files +226 −34
  1. pinheadmz commented at 2:22 PM on July 15, 2026: member

    Introduces a new configuration option -rpcmaxconnections with default value 128. This is used to limit the number of simultaneous HTTPClient connected to the HTTPServer. When the limit is reached, new pending connections remain queued in the kernel's socket buffer. Those connections have complete TCP handshakes with the kernel but do not occupy any application memory.

    The previous libevent-based HTTP server had no limit on connections but it did have a limit on the kernel socket queue:

    https://github.com/libevent/libevent/blob/e7ff4ef2b4fc950a765008c18e74281cdb5e7668/http.c#L3510

    	if (listen(fd, 128) == -1) {
    

    The current HTTP server, like the p2p server, uses a platform constant here:

    https://github.com/bitcoin/bitcoin/blob/b6becf3534c7b7f1b4d356a8f6113d62b6dd05bf/src/httpserver.cpp#L743

    (on my macOS SOMAXCONN is 128 but on my Debian machine it's 4096)

    The default of 128 was chosen as a reasonable upper bound for RPC use cases. Systems designed to handle more simultaneous HTTP connections than this (previously relying on the absence of a limit) can adjust the setting.

    File descriptors

    Because of the connection limit, we can now account for the maximum number of file descriptors needed by the HTTP server. This addresses several issues (#11368 #11322 maybe #27732) that could have been fixed by a PR waiting in vain for a libevent release (#27731).

    Bonus performance improvement

    The new limit is managed in a loop that drains the kernel's socket queue with accept(). All pending connections from the queue (up to the limit) are processed in one single call to SocketHandlerListening(). The previous code would only accept one connection from the queue on each I/O loop tick, with a SELECT_TIMEOUT (50ms) sleep between each.

  2. DrahtBot added the label RPC/REST/ZMQ on Jul 15, 2026
  3. DrahtBot commented at 2:23 PM on July 15, 2026: contributor

    <!--e57a25ab6845829454e8d69fc972939a-->

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    <!--006a51241073e994b41acfe9ec718e94-->

    Code Coverage & Benchmarks

    For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/35730.

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline and AI policy for information on the review process.

    Type Reviewers
    ACK winterrdog, fjahr
    Concept ACK furszy, brunoerg, stickies-v, willcl-ark
    Stale ACK janb84

    If your review is incorrectly listed, please copy-paste <code>&lt;!--meta-tag:bot-skip--&gt;</code> into the comment that the bot should ignore.

    <!--174a7506f384e20aa4161008e828411d-->

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #35852 (scripted-diff: Use inline const(expr) over static constexpr in headers by maflcko)
    • #35591 ([DO NOT MERGE] Erlay: bandwidth-efficient transaction relay protocol (Full implementation) by sr-gi)
    • #35037 (ipc: support per-address max-connections options on -ipcbind by enirox001)
    • #34978 (init: reserve file descriptors for IPC connections by enirox001)

    If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

    <!--5faf32d7da4f0f540f40219e4f7537a3-->

  4. furszy commented at 2:26 PM on July 15, 2026: member

    Concept ACK

  5. fjahr commented at 2:38 PM on July 15, 2026: contributor

    Concept ACK

  6. brunoerg commented at 3:32 PM on July 15, 2026: contributor

    Concept ACK

  7. in src/httpserver.cpp:1231 in 937110f08f outdated
    1227 | @@ -1228,6 +1228,7 @@ bool InitHTTPServer()
    1228 |      g_http_server = std::make_unique<HTTPServer>(MaybeDispatchRequestToWorker);
    1229 |  
    1230 |      g_http_server->SetServerTimeout(std::chrono::seconds(gArgs.GetIntArg("-rpcservertimeout", DEFAULT_HTTP_SERVER_TIMEOUT)));
    1231 | +    g_http_server->SetMaxConnections(std::max(gArgs.GetArg<int>("-rpcmaxconnections", DEFAULT_MAX_HTTP_CONNECTIONS), 1));
    


    brunoerg commented at 5:11 PM on July 15, 2026:

    937110f08f00421f6af7cccf627abab79c3c291b: nit: Is it worth to add an upper bound limit on it as well? Just to avoid a huge int value on it due to min_required_fds.


    pinheadmz commented at 5:50 PM on July 16, 2026:

    If the platform can't provide enough file descriptors, bitcoin will raise an InitError. But I will make sure the setting is int-safe in init.cpp

  8. in src/init.cpp:1073 in 36da6684cc outdated
    1069 | +    int nRPCBind = std::max(args.GetArgs("-rpcbind").size(), size_t(2));
    1070 | +    // HTTP server connected client sockets
    1071 | +    int rpc_max_connections = std::max(args.GetArg<int>("-rpcmaxconnections", DEFAULT_MAX_HTTP_CONNECTIONS), 1);
    1072 |      // Reserve enough FDs to account for the bare minimum, plus any manual connections, plus the bound interfaces
    1073 | -    int min_required_fds = MIN_CORE_FDS + MAX_ADDNODE_CONNECTIONS + nBind;
    1074 | +    int min_required_fds = MIN_CORE_FDS + MAX_ADDNODE_CONNECTIONS + nBind + nRPCBind + rpc_max_connections;
    


    brunoerg commented at 5:21 PM on July 15, 2026:

    36da6684cc61a960064aa369ecfe3a86610e2566: If -server=0 I don't think we should increase min_required_fds this way.


    willcl-ark commented at 10:30 AM on July 16, 2026:

    In 36da6684cc61a960064aa369ecfe3a86610e2566

    Is it OK to add these even if we are running with -server=0?


    willcl-ark commented at 10:44 AM on July 16, 2026:

    Oh, reading the earlier review I see bruno has flagged this too. I should have perhaps done that first :'(


    pinheadmz commented at 6:47 PM on July 17, 2026:

    Good call, will ignore HTTP fd's if server=0 and cover with a test. Adding this in an extra commit.


    fjahr commented at 3:27 PM on July 30, 2026:

    nit: Could have made this calculation above total_fds and added the numbers? That would have made this a easier to reason about.

  9. in test/functional/interface_http.py:578 in 937110f08f
     591 | -        ):
     592 | -            # Fill connection slots
     593 | -            for i in range(1, MAX_HTTP_CONNECTIONS + 1):
     594 | +        for comment, extra_args, limit in [
     595 | +            ("default (128)", [], 128),
     596 | +            ("-rpcmaxconnections=18", ["-rpcmaxconnections=18"], 18)
    


    brunoerg commented at 5:31 PM on July 15, 2026:

    937110f08f00421f6af7cccf627abab79c3c291b: Perhaps it could set -rpcservertimeout=0 so that the connections filling the slots are never disconnected while the test is still running.


    pinheadmz commented at 2:55 PM on July 16, 2026:

    Oh yeah great idea! Adding -rpcservertimeout=0

  10. in test/functional/interface_http.py:593 in 937110f08f outdated
     606 | +
     607 | +            MAX_HTTP_CONNECTIONS = limit
     608 | +            connections = []
     609 | +
     610 | +            # No errors logged
     611 | +            with self.node.assert_debug_log(
    


    brunoerg commented at 5:43 PM on July 15, 2026:

    937110f08f00421f6af7cccf627abab79c3c291b: Curious fact: method=invalidrpc_1 is a substring of the log line for request 10-19 and 100–127 - e.g. invalidrpc_1 is a prefix of invalidrpc_128. So the check "method=invalidrpc_1" in log returns True even if request 1 was never sent.


    pinheadmz commented at 2:59 PM on July 16, 2026:

    Good point, I'll include a trailing space so ...=1 " is distinct from ...=10 "

  11. stickies-v commented at 9:52 AM on July 16, 2026: contributor

    Concept ACK

  12. in src/init.cpp:1071 in 36da6684cc outdated
    1064 | @@ -1065,8 +1065,12 @@ bool AppInitParameterInteraction(const ArgsManager& args)
    1065 |      const size_t max_private{args.GetBoolArg("-privatebroadcast", DEFAULT_PRIVATE_BROADCAST)
    1066 |                               ? MAX_PRIVATE_BROADCAST_CONNECTIONS
    1067 |                               : 0};
    1068 | +    // HTTP server listen sockets: by default two (IPv4 and IPv6 loopback), or one per -rpcbind entry
    1069 | +    int nRPCBind = std::max(args.GetArgs("-rpcbind").size(), size_t(2));
    1070 | +    // HTTP server connected client sockets
    1071 | +    int rpc_max_connections = std::max(args.GetArg<int>("-rpcmaxconnections", DEFAULT_MAX_HTTP_CONNECTIONS), 1);
    


    willcl-ark commented at 10:38 AM on July 16, 2026:

    In 36da6684cc61a960064aa369ecfe3a86610e2566

    Don't percieve this as introduced here, but what happens if -rpcconnections > INT_MAX?


    pinheadmz commented at 8:50 PM on July 16, 2026:

    Good catch, I'll add a check to ensure that the total file descriptors the user needs doesn't overflow int, which is the type expected by RaiseFileDescriptorLimit()

    But you're also right, this isn't introduced here. For example -rpcthreads=999999999999 would get clamped at 2^32, but still crash while actually trying to start that many threads.

    New behavior for this setting (with ulimit -n 165):

    # Exceed OS limit
    --> bcd -rpcmaxconnections=4
    Error: Not enough file descriptors available. 165 available, 166 required.
    --> bcd -rpcmaxconnections=3 -bind=::1:10000 -bind=::1:20000
    Error: Not enough file descriptors available. 165 available, 166 required.
    
    # Setting would overflow max int before even checking OS
    --> bcd -rpcmaxconnections=99999999999999999
    Error: Too many file descriptors requested. Try lower values for -rpcmaxconnections or -maxconnections, or fewer settings of -rpcbind, -bind and -whitebind
    
  13. in src/httpserver.cpp:925 in 962ff07a61 outdated
     925 | +            // Stop early if the kernel queue is empty (AcceptConnection returns null)
     926 | +            // or if accepting the last connection brought us to the limit.
     927 | +            while (GetConnectionsCount() < MAX_HTTP_CONNECTIONS) {
     928 | +                CService addr_accepted;
     929 | +                auto sock_accepted{AcceptConnection(*sock, addr_accepted)};
     930 | +                if (!sock_accepted) break;
    


    willcl-ark commented at 10:39 AM on July 16, 2026:

    In 962ff07a61eedcca02ce479101ea4b5d582fac72

    This is nice!

    could (but perhaps also should?) we add a test that opens multiple connections concurrently, verifies the accept loop drains them up to the limit, and then confirms a queued connection is accepted after one slot is freed? The current test opens connections sequentially AFAICT.


    pinheadmz commented at 2:51 PM on July 16, 2026:

    The functional test opens connections one at a time in a single thread, but then they all remain open concurrently. While open, one extra connection is tested and expected to time out. What I don't have yet is testing that one extra connection with a much longer time out, does finally get accepted and responded to... so I will add that.

  14. willcl-ark commented at 10:43 AM on July 16, 2026: member

    Concept ACK!

  15. b-l-u-e commented at 3:23 PM on July 16, 2026: contributor

    Tested on Windows 11 Pro with native MSVC bitcoind v31.99.0.

    -rpcmaxconnections=4 -rpcservertimeout=30 connection queued okay no refusal or reset, no response or log activity while all slots were occupied then HTTP 200 about 1.3 ms after a slot was freed. Idle slots were reclaimed after about 30.4s.

    The Backlog results:

    • 32attempts and 64 attempts: all connected
    • 128 attempts: 104 connected, 24 timed out
    • 256 attempts: 204 connected, 52 timed out

    and also all failures were TimeoutError; none refused or reseted

  16. in test/functional/interface_http.py:607 in 36da6684cc
     602 | +
     603 | +            # Over the limit, expect rejection
     604 | +            with self.node.assert_debug_log(
     605 | +                expected_msgs = [],
     606 | +                unexpected_msgs = ["method=never_accepted"]
     607 | +            ):
    


    b-l-u-e commented at 3:38 PM on July 16, 2026:

    here comment says over the limit, expect rejection but when at capacity the server queues in the listen backlog and the client hangs. i think comment should say like hang or timeout


    pinheadmz commented at 7:09 PM on July 17, 2026:

    You're right! My original approach disconnected excessive clients instead of leaving them in the kernel queue. I'll fix the comment.

  17. pinheadmz force-pushed on Jul 17, 2026
  18. pinheadmz commented at 7:25 PM on July 17, 2026: member

    push to 779e722fba36098deef75329346b132ad1972243

    Address review feedback, mostly improving test coverage but also improved handling of excessive rpcmaxconnections values and not reserving file descriptors if the HTTP server is disabled.

  19. in doc/release-notes-35182.md:21 in 779e722fba outdated
      13 | @@ -14,3 +14,8 @@ Certain HTTP edge cases will observe different behavior to be more RFC-compliant
      14 |  - "Line Folding" is rejected (whitespace at start of a header line)
      15 |  - Tolerate `%` at the end of requested URLs
      16 |  - Multiple "Content-Length" headers with different values are rejected
      17 | +
      18 | +A new configuration option `-rpcmaxconnections` (default `128`) limits the
      19 | +number of simultaneously connected HTTP clients to the server. The application
      20 | +will now attempt to reserve file descriptors for the HTTP server sockets. If your
      21 | +system has limited resources, consider using a lower setting.
    


    b-l-u-e commented at 3:44 AM on July 20, 2026:

    nit:

    diff --git a/doc/release-notes-35182.md b/doc/release-notes-35182.md
    index d7c22e2f03..2c05b5dc3d 100644
    --- a/doc/release-notes-35182.md
    +++ b/doc/release-notes-35182.md
    @@ -1,5 +1,4 @@
    -HTTP: RPC / REST
    -----------------
    +## HTTP: RPC / REST
     
     The HTTP server has been rewritten from scratch to replace libevent. (#35182)
     
    @@ -16,6 +15,9 @@ Certain HTTP edge cases will observe different behavior to be more RFC-compliant
     - Multiple "Content-Length" headers with different values are rejected
     
     A new configuration option `-rpcmaxconnections` (default `128`) limits the
    -number of simultaneously connected HTTP clients to the server. The application
    -will now attempt to reserve file descriptors for the HTTP server sockets. If your
    -system has limited resources, consider using a lower setting.
    +number of simultaneously connected HTTP clients to the server. When the limit
    +is reached, additional clients are not immediately rejected with an HTTP
    +error the connection may wait until a slot frees or until the client itself
    +times out. The application will also attempt to reserve file descriptors for
    +the HTTP server sockets. If your system has limited resources, consider using
    +a lower `-rpcmaxconnections` setting.
    

    pinheadmz commented at 1:35 PM on July 21, 2026:

    I think adding a line about queing connections after the server is full is justified, will add on next rebase

  20. b-l-u-e commented at 4:13 AM on July 20, 2026: contributor

    so far we have a test that covers connections one at a time..Could we add another test that opens several connections at once?

  21. in test/functional/interface_http.py:631 in 779e722fba outdated
     626 | +            def wait_for_send(conn):
     627 | +                return conn.get('/rest/blockhashbyheight/0.json').read()
     628 | +
     629 | +            conn = BitcoinHTTPConnection(self.node)
     630 | +            conn.set_timeout(None)
     631 | +            executor = concurrent.futures.ThreadPoolExecutor(max_workers=1)
    


    fjahr commented at 4:07 PM on July 20, 2026:

    nit: This launches one thread per run of this big for loop and never closes them until the test is finished. It's not that many so it isn't that bad but using with here would be cleaner:

     with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
    

    But since this only loops twice and there is nothing else happing afterwards this isn't that important.

  22. pinheadmz commented at 1:37 PM on July 21, 2026: member

    so far we have a test that covers connections one at a time..Could we add another test that opens several connections at once?

    Not sure what you mean, check_connection_limit() in interface_http opens 128 connections then opens an additional connection two times (once expecting timeout, another waiting until a slot opens). Those connections are opened in a single python thread, but then they are all open at the same time, occupying server slots, file descriptors, sockets, etc. So I suppose the test could be modified to open those 128 connections in simultaneous parallel threads? But I don't think that would improve the test coverage.

  23. janb84 commented at 12:21 PM on July 22, 2026: contributor

    cr ACK 779e722fba36098deef75329346b132ad1972243

    LGTM!

    During review I discovered that ClientAllowed() does not actively disconnects the client, but awaits the timeout / disconnect from the client. The current PR limits the number of active connections which makes waiting for timeouts / client disconnects a (increased) DDOS vector, I have created a followup PR #35772 to address this. #35592 will mitigate the issue.

  24. DrahtBot requested review from fjahr on Jul 22, 2026
  25. DrahtBot requested review from willcl-ark on Jul 22, 2026
  26. DrahtBot requested review from brunoerg on Jul 22, 2026
  27. DrahtBot requested review from stickies-v on Jul 22, 2026
  28. DrahtBot requested review from furszy on Jul 22, 2026
  29. in src/init.cpp:1085 in 779e722fba
    1082 | +    // RaiseFileDescriptorLimit() accepts an int so we check that limit before casting.
    1083 | +    const int64_t total_fds = int64_t{MIN_CORE_FDS} +
    1084 | +                              MAX_ADDNODE_CONNECTIONS +
    1085 | +                              nBind +
    1086 | +                              nRPCBind +
    1087 | +                              rpc_max_connections +
    


    fjahr commented at 3:56 PM on July 30, 2026:

    I didn't keep up with all the http follow-ups in the last couple of weeks, so sorry if I am missing something here. But making the max rpc connections part of the minimum fds means that the minimum fds is raised significantly. I am not sure if we still support systems where this is a problem and the default setting wouldn't start under usual circumstances. But it feels weird to require a maximum that will likely be never reached for any usual usage as part of the global minimum.

    What would seem more natural to me but requires a bit of further restructuring is something like this:

    base_fds = core + addnode + p2p_binds + rpc_binds;
    min_http_connections = std::min(requested_rpc_connections, 16);
    min_required_fds = base_fds + min_http_connections;
    
    if (available_fds < min_required_fds) {
        return InitError(...);
    }
    
    int remaining = available_fds - min_required_fds;
    
    // Prioritize ordinary P2P capacity.
    nMaxConnections = std::min(remaining, user_max_connection);
    remaining -= nMaxConnections;
    
    // Use remaining fd capacity to raise HTTP above the min.
    effective_rpc_max_connections =
        min_http_connections +
        std::min(remaining, requested_rpc_connections - min_http_connections);
    

    So we are checking if we have an actual minimum available that should be sufficient rather than the maximum and depending on what's available we take what we get up until the maximum.


    fjahr commented at 6:18 PM on July 30, 2026:

    I guess a downside of this is that we can't clearly communicate the number of available connections and users would rather need to find this number in a log output. But I am unsure how actionable the exact number really is. If you are going to hammer our http server and you have no idea what you are doing then you wouldn't know what to do with that and if you know what you are doing you might as well find the relevant number in the logs. And the number alone doesn't tell you alone what the perfect rate limiting is, there will likely always be some trial and error involved if you are doing something crazy.


    pinheadmz commented at 7:35 PM on July 31, 2026:

    Yeah I agree this should managed better.

    First, I like starting with a default of 16 instead of 128. That seems reasonable for most individual users and systems with more or less capacity can adjust up or down.

    Next, in the case where the system can not provide all the FDs the user has requested we can sort of triage:

    1. If they set rpcmaxconnections above the default of 16 we could abort and tell the user to reconsider their settings on their platform.
    2. If they didn't configure rpcmaxconnections at all, we continue existing behavior which is to reduce p2p connections, log, and continue. Upgrading to this PR will bring that threshold down by 16.

    So the users impacted most by this will see their connection count drop by 16 -- but those users were also dangerously close to crashing their system by making too many RPC requests, right?


    fjahr commented at 8:57 PM on July 31, 2026:

    Sounds good to me to do it like this.

    but those users were also dangerously close to crashing their system by making too many RPC requests, right?

    I would think so. I can't really imagine hitting 16 with anything other than a naive script that is sending tons of requests. Anyone who has a serious use-case where a higher value actually makes sense should be able to increase the max in configs.


    pinheadmz commented at 6:53 PM on August 6, 2026:

    But making the max rpc connections part of the minimum fds means that the minimum fds is raised significantly.

    Just occurred to me that recent merge #28463 also added 75 file descriptors to the initial reservation attempt (although we also tune it down if they aren't available).

    SO maybe it's reasonable to set the default to more like 32 or 64? There's probably a sweet spot where users wont run out of file descriptors, and big services won't need to boost the setting either.


    fjahr commented at 8:54 PM on August 11, 2026:

    Yeah, for #28463 it's part of the reservation attempt and I think peer connections aren't really comparable here. More connections should be good for any node and the network as a whole (if the node can manage it of course). The assumption is also IMO that these have a pretty high chance of being utilized over time in an average node. But I don't see why an average node would need more than 16 rpc connections. And the ones that do need it can raise the limit for their individual use case, the network does not play a role in this.

    There's probably a sweet spot where users wont run out of file descriptors, and big services won't need to boost the setting either.

    Hm, what I think of in terms of "big services" actually doesn't need that many connections I think. Mempool.space seems to use ephemeral connections for example, stuff like fulcrum etc. typically uses low numbers. The biggest threat to the 16 connections' number may actually be some Start9/Umbrel node that has a maxed-out number of services running locally so that they add up over time. It seems hard to predict, maybe there could be an approximation if they publish statistics from their app stores but I didn't check that. I guess if you have a lightning node, btcpayserver, an explorer and something else you could potentially run out so raising to 32 would be fine by me as a precaution. I liked 16 primarily because it matches the worker threads default so having more should not really help with response times. Maybe that is another approach that could be considered: using num worker threads + small X as the default unless configured explicitly. Maybe it's overengineering, though, and makes the behavior harder to grasp for users.


    fjahr commented at 8:59 PM on August 11, 2026:

    (just now was reading on and saw you checked specific numbers of services as well, great!)

    #27731 set the limit to workQueueDepth * 2) which would be 128, but didn't reserve those file descriptors in init.cpp, so the assumption was just that those FDs would be available anyway.

    I don't think I thought about that at the time, it was just a proof of concept that the immediate crashes that were observed could be fixed as I was able to reproduce them with the new libevent release that never came.

  30. fjahr commented at 6:12 PM on July 30, 2026: contributor

    Looks good to me modulo my question about the approach around the minimum required file descriptors.

  31. DrahtBot requested review from fjahr on Jul 30, 2026
  32. in src/httpserver.cpp:926 in 779e722fba
     926 | +            // or if accepting the last connection brought us to the limit.
     927 | +            while (GetConnectionsCount() < static_cast<size_t>(m_rpcmaxconnections)) {
     928 | +                CService addr_accepted;
     929 | +                auto sock_accepted{AcceptConnection(*sock, addr_accepted)};
     930 | +                if (!sock_accepted) break;
     931 |                  NewSockAccepted(std::move(sock_accepted), addr_accepted);
    


    winterrdog commented at 11:26 AM on August 1, 2026:

    one thing i wanted to ask about is the accept logic, specifically around possible starvation.

    now that SocketHandlerListening() drains a listening socket's backlog up to the connection cap before moving on to the next socket in m_listen (greedy by design), i am wondering if this could introduce starvation when more than one listening socket is active (e.g. IPv4 and IPv6 loopback, like in the default case). in that case, could a single socket's backlog end up consuming the entire connection cap, effectively keeping out the other socket's queue from being serviced while the first one remains busy?

    is there any plan for handling it for example, rotating which socket gets drained first, or distributing accepts more evenly across sockets per pass for a single tick through a round-robin kind of style? or is this just fine ?


    pinheadmz commented at 12:11 PM on August 5, 2026:

    is there any plan for handling it

    I haven't really thought about it but we shouldn't have to protect the HTTP sockets as vigorously as the P2P connections. P2P, makes sense to assume everything is an attack. HTTP, the software has always assumed all connections are friendly (but might be misconfigured).

    If a user makes so many keep-alive connections to ::1 then opens an issue about their 127.0.0.1 connections timing out, I don't even know if we would address it as a software flaw.

    These are my shallow thoughts but we can discuss it further and maybe decide there is something worth implementing


    winterrdog commented at 10:55 PM on August 6, 2026:

    good points, and thanks for engaging with it.

    but we shouldn't have to protect the HTTP sockets as vigorously as the P2P connections. P2P, makes sense to assume everything is an attack.

    to be honest this is probably more of a nit than a real concern (only realised it after reading your comment), since both approaches end up accepting the same connections in total, just in a different order.

    what i had in mind is small: it just swaps which loop is on the outside. right now it is "for each listening socket, accept connections from it until either its queue is empty or the cap is hit, then move to the next socket." the alternative is "make one pass across all listening sockets, accepting at most one connection per socket per pass, and keep repeating passes until the cap is hit or no socket yields a connection." same total number of connections accepted, same end state, just a different order

    it is a bit like DFS vs BFS over the list of listening sockets, where each accepted connection is a node being visited:

    current (DFS-ish): drain one socket's connections fully before moving on to the next socket
    
      socket A connections: [x][x][x][x][x][x] ..... (all accepted first)
      socket B connections: [ ][ ][ ][ ][ ][ ] ..... (none accepted yet, waiting its turn)
    
    alternative (BFS-ish): accept one connection per socket per pass, then repeat
    
      pass 1:  socket A: 1 connection accepted   socket B: 1 connection accepted
      pass 2:  socket A: 1 connection accepted   socket B: 1 connection accepted
      pass 3:  socket A: 1 connection accepted   socket B: 1 connection accepted
      ...
    

    the BFS-style version does not accept connections any faster in total, but it means one socket's backlog of pending connections can never hold up another socket's pending connections for the whole tick. at any point mid-tick, the gap between how many connections have been accepted from Socket A versus Socket B is at most one. from the outside, this makes accepting connections look more evenly spread across bound addresses, even though the total number of accept calls and the total number of connections accepted by the end of the tick are identical

    <details> <summary>the implementation i had in mind </summary>

    void HTTPServer::SocketHandlerListening(const Sock::EventsPerSock& events_per_sock) {
        if (m_stop_accepting) return;
        bool accepted_any = true;
    
        // keep making passes over all listening sockets, within this single
        // tick, for as long as connections are still being accepted and
        // the cap has not been hit. this is what lets the server drain a
        // full backlog in one tick instead of waiting for the next tick's
        // sleep/wake cycle. it stops as soon as a whole pass yields
        // nothing (all queues genuinely empty) or the connection cap is
        // reached, whichever comes first
        while (accepted_any && GetConnectionsCount() < static_cast<size_t>(m_rpcmaxconnections)) {
            accepted_any = false;
    
            // give every listening socket one accept attempt per pass, in
            // order, before any socket gets a second attempt. this keeps
            // the gap between how many connections each socket has had
            // accepted at 'at most' one, so a socket with a large backlog
            // cannot starve out(presumably) another socket's pending
            // connections within the same tick - real world might play out
            // different depending on how complex
            //
            // CAVEAT about the 'presumed starvation': in practice this
            // mostly matters when more than one listening socket is active
            // (e.g. multiple -rpcbind addresses). most real-world setups
            // bind to just one or two addresses (default loopback
            // IPv4/IPv6), so the fairness benefit here is modest for
            // typical deployments and becomes more relevant only for the
            // less common case of binding to several network
            // interfaces/addresses at once
            for (const auto& sock : m_listen) {
                if (m_interrupt_net) return;
                if (GetConnectionsCount() >= static_cast<size_t>(m_rpcmaxconnections)) {
                    break;
                }
    
                const auto it = events_per_sock.find(sock);
                if (it == events_per_sock.end() || !(it->second.occurred & Sock::RecvEvent)) {
                    continue;
                }
    
                CService addr_accepted;
                auto sock_accepted{AcceptConnection(*sock, addr_accepted)};
                if (sock_accepted) {
                    NewSockAccepted(std::move(sock_accepted), addr_accepted);
                    accepted_any = true;
                }
            }
        }
    }
    

    </details>

    If a user makes so many keep-alive connections to ::1 then opens an issue about their 127.0.0.1 connections timing out, I don't even know if we would address it as a software flaw.

    πŸ’―


    pinheadmz commented at 1:33 PM on August 7, 2026:

    I understand what you're saying but I don't think it's worth the refactor. There is a slight cost for waiting until the next I/O cycle, reconstructing the list of listening sockets and checking for events. I think users are much more likely to only use the same socket for all their clients, assuming thy even use more than one. So draining the queue on each tick is best.

  33. winterrdog commented at 11:32 AM on August 1, 2026: contributor

    ACK 779e722fba36098deef75329346b132ad1972243

    good follow-through from #35592. this PR adds a new layer on top of that: allowed clients cannot happily pile up connections without a limit anymore, since there is now a hard cap on how many can be connected at once. nice defense-in-depth pairing i.e. #35592 decides who can hold a slot, this one controls how many slots can exist in total

  34. DrahtBot added the label Needs rebase on Aug 1, 2026
  35. pinheadmz force-pushed on Aug 5, 2026
  36. pinheadmz commented at 1:49 PM on August 5, 2026: member

    push to 94950bd399d8484be86efde608c9af8c10cf9baf

    • rebase on master, fix conflict from #35592
    • reduce default HTTP max connections to 16 to conserve file descriptors
    • prioritize p2p file descriptors by aborting if -rpcmaxconnections is above the default

    I also added a scripted diff commit to clean up some variable names in init.cpp so the old code and new code look right together.

    I think 16 is an ok amount for simultaneous HTTPRemoteClients given that bitcoin-cli uses ephemeral (not keep-alive) connections. The default value would only need to be adjusted by a service that uses more than 16 persistent connections. I'm not even sure if software like LND for example uses persistent or ephemeral connections to Bitcoin Core, but I am checking that now...

    update:

    Hmmm... #27731 set the limit to workQueueDepth * 2) which would be 128, but didn't reserve those file descriptors in init.cpp, so the assumption was just that those FDs would be available anyway.

    I had an agent scan the codebase of a few HTTP consumers to see if they use ephemeral or persistent connections and the results are below.

    If an application uses persistent connections, a user could only run a few of those applications in parallel against on bitcoin code node with the default value.

    If an application uses ephemeral connections that makes a lot more room for other applications to make connections unless an application needs makes more than 16 ephemeral connections at once. Again, easily adjusted by setting -rpcmaxconnections)

    Project Connection Style Max Simultaneous Conns Code Link
    lnd Ephemeral 1 per RPC call chainreg/chainregistry.go:388-420 β€” rpcclient.New(rpcConfig, nil) with HTTPPostMode: true. btcd creates a new http.Client per request; issue https://github.com/btcsuite/btcd/issues/1323 confirms no connection reuse.
    rpc-bitcoin Ephemeral 1 per RPC call node_modules/rpc-request/build/src/rpc.js:3 β€” uses request-promise-native. No forever: true or custom http.Agent configured. Node.js http.Agent defaults to keepAlive: false.
    electrs Ephemeral 1 per RPC call Cargo.toml:28 β€” depends on bitcoincore-rpc β†’ jsonrpc crate β†’ minreq HTTP backend. minreq issue https://github.com/neonmoe/minreq/issues/75 confirms no persistent connections.
    eclair Keep-alive 5 idle (OkHttp default) Setup.scala:85 β€” OkHttpFutureBackend(). OkHttp pools 5 idle connections per host, 5-min keep-alive.
    corepc Keep-alive Configurable (10–100) bitreq/src/client.rs:1-4 β€” "Connection pooling client… caches connections to avoid repeated TCP handshakes." Client::new(capacity) with LRU eviction.
    bitcoinjs-lib Ephemeral 1 per RPC call test/integration/_regtest.ts:1 β€” uses regtest-client β†’ request-promise-native. Same as rpc-bitcoin: no keep-alive agent configured.

    Key takeaway: The 4 ephemeral clients open and close connections in milliseconds β€” they never hold more than 1 connection at a time per caller. The 2 keep-alive clients have small, bounded pools (5 for eclair, configurable for corepc). A 16-connection cap is not a problem for any of these projects under normal operation.

  37. DrahtBot removed the label Needs rebase on Aug 5, 2026
  38. fanquake added this to the milestone 32.0 on Aug 10, 2026
  39. DrahtBot added the label Needs rebase on Aug 10, 2026
  40. pinheadmz force-pushed on Aug 10, 2026
  41. pinheadmz commented at 3:34 PM on August 10, 2026: member

    push to 14356246c3299424d6c026ccbccf642394bffb91

    rebase on master and fix conflict with #34794

  42. DrahtBot removed the label Needs rebase on Aug 10, 2026
  43. winterrdog commented at 6:24 PM on August 10, 2026: contributor

    re-ACK 14356246c3299424d6c026ccbccf642394bffb91

  44. DrahtBot requested review from janb84 on Aug 10, 2026
  45. in src/init.cpp:1083 in 14356246c3
    1085 | -    // Try raising the FD limit to what we need (available_fds may be smaller than the requested amount if this fails)
    1086 | -    available_fds = RaiseFileDescriptorLimit(user_max_connection + max_private + min_required_fds);
    1087 | +    // HTTP server listen sockets: by default two (IPv4 and IPv6 loopback), or one per -rpcbind entry
    1088 | +    int num_rpc_bind = std::max(args.GetArgs("-rpcbind").size(), size_t(2));
    1089 | +    // HTTP server connected client sockets
    1090 | +    int user_rpc_max_connections = std::max(args.GetArg<int>("-rpcmaxconnections", DEFAULT_MAX_HTTP_CONNECTIONS), 1);
    


    janb84 commented at 3:04 PM on August 11, 2026:

    Upon closer inspection I wonder if this is intentional, if I set -rpcmaxconnections=0 it will silently set it to 1
    NIT, be explicit that it has to be set to greater than zero

        int user_rpc_max_connections = args.GetArg<int>("-rpcmaxconnections", DEFAULT_MAX_HTTP_CONNECTIONS);
        if (user_rpc_max_connections < 1) {
            return InitError(Untranslated("-rpcmaxconnections must be greater than zero"));
        }
    

    pinheadmz commented at 2:27 PM on August 12, 2026:

    Good call, taking this and also referring the user to server=0 in case that's what they really meant to do...


    fjahr commented at 10:48 PM on August 13, 2026:

    nit: Would have been nice to add very simple coverage for this as well

  46. DrahtBot requested review from janb84 on Aug 11, 2026
  47. fjahr commented at 9:05 PM on August 11, 2026: contributor

    utACK 14356246c3299424d6c026ccbccf642394bffb91

    It would seem reasonable for the me to raise the limit to 32 out of an abundance of caution for the node power user case I described above but leaving the limit at 16 seems also fine for most sane use cases. Something dynamic based on num worker threads would be fine as well but probably overcomplicates things. Happy to re-review if you make another change here.

  48. pinheadmz force-pushed on Aug 12, 2026
  49. pinheadmz commented at 2:41 PM on August 12, 2026: member

    push to 0822ca4d1b4a1962a8bf62fc0ad47a511db81d1a

    require that -rpcmaxconnections > 0

  50. DrahtBot added the label CI failed on Aug 12, 2026
  51. scripted-diff: Rename nUserBind, nBind, nMaxConnections to snake_case
    -BEGIN VERIFY SCRIPT-
    sed -i 's/\bnUserBind\b/num_user_p2p_bind/g' src/init.cpp
    sed -i 's/\bnBind\b/num_p2p_bind/g' src/init.cpp
    sed -i 's/\bnMaxConnections\b/num_p2p_max_connections/g' src/init.cpp
    sed -i 's/\buser_max_connection\b/user_p2p_max_connections/g' src/init.cpp
    -END VERIFY SCRIPT-
    58662e668b
  52. http: limit connected clients to 16 fc5c48635a
  53. http: configure simultaneous connection limit with -rpcmaxconnections b29affb613
  54. init: account for maximum file descriptors needed by HTTP 4838c65e2c
  55. init: do not count file descriptors for HTTPServer if -server=0 1f985995af
  56. pinheadmz force-pushed on Aug 12, 2026
  57. pinheadmz commented at 7:56 PM on August 12, 2026: member

    push to 1f985995afe7c9c536973c8af9fd7ff72902e2cb

    rebase on master to pull in CI fix from #35867

  58. DrahtBot removed the label CI failed on Aug 12, 2026
  59. winterrdog commented at 5:06 AM on August 13, 2026: contributor

    re-ACK 1f985995afe7c9c536973c8af9fd7ff72902e2cb

  60. fjahr commented at 10:48 PM on August 13, 2026: contributor

    utACK 1f985995afe7c9c536973c8af9fd7ff72902e2cb

  61. in src/init.cpp:1081 in 4838c65e2c
    1080 | -    int min_required_fds = MIN_CORE_FDS + MAX_ADDNODE_CONNECTIONS + num_p2p_bind;
    1081 |  
    1082 | -    // Try raising the FD limit to what we need (available_fds may be smaller than the requested amount if this fails)
    1083 | -    available_fds = RaiseFileDescriptorLimit(user_p2p_max_connections + max_private + min_required_fds);
    1084 | +    // HTTP server listen sockets: by default two (IPv4 and IPv6 loopback), or one per -rpcbind entry
    1085 | +    int num_rpc_bind = std::max(args.GetArgs("-rpcbind").size(), size_t(2));
    


    willcl-ark commented at 10:56 AM on August 14, 2026:

    In 4838c65e2c98bd98b4116d467bba4e10f7789ec9

    We do appear to reserve a minimum of 2 here, even if a single -rpcbind entry is specified so this is very slightly inaccurate (on the conservative side)

  62. willcl-ark commented at 11:14 AM on August 14, 2026: member

    I seem to get local a test failure in feature_init.py where the fds are not reduced as expected:

    <details> <summary>Details</summary>

    Temporary test directory at /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555
    Remaining jobs: [feature_init.py]
    1/1 - feature_init.py failed, Duration: 12 s
    
    stdout:
    2026-08-14T11:05:55.352575Z TestFramework (INFO): PRNG seed is: 47629099074133609
    2026-08-14T11:05:55.403156Z TestFramework (INFO): Initializing test directory /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0
    2026-08-14T11:05:55.764639Z TestFramework (INFO): Test specifying custom pid file via -pid command line option
    2026-08-14T11:05:55.764725Z TestFramework (INFO): -> path relative to datadir (my_fancy_bitcoin_pid_file.foobar)
    2026-08-14T11:05:56.318691Z TestFramework (INFO): -> absolute path (/mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/my_fancy_bitcoin_pid_file.foobar)
    2026-08-14T11:05:57.171391Z TestFramework (INFO): Starting node and will terminate after line b'Validating signatures for all blocks'
    2026-08-14T11:05:57.333458Z TestFramework (INFO): Starting node and will terminate after line b'scheduler thread start'
    2026-08-14T11:05:57.496677Z TestFramework (INFO): Starting node and will terminate after line b'Starting HTTP server'
    2026-08-14T11:05:57.659722Z TestFramework (INFO): Starting node and will terminate after line b'Loading P2P addresses'
    2026-08-14T11:05:57.822187Z TestFramework (INFO): Starting node and will terminate after line b'Loading banlist'
    2026-08-14T11:05:57.985044Z TestFramework (INFO): Starting node and will terminate after line b'Loading block index'
    2026-08-14T11:05:58.147804Z TestFramework (INFO): Starting node and will terminate after line b'Checking all blk files are present'
    2026-08-14T11:05:58.310305Z TestFramework (INFO): Starting node and will terminate after line b'Loaded best chain:'
    2026-08-14T11:05:58.473197Z TestFramework (INFO): Starting node and will terminate after line b'init message: Verifying blocks'
    2026-08-14T11:05:58.635943Z TestFramework (INFO): Starting node and will terminate after line b'init message: Starting network threads'
    2026-08-14T11:05:58.813151Z TestFramework (INFO): Starting node and will terminate after line b'net thread start'
    2026-08-14T11:05:59.041523Z TestFramework (INFO): Starting node and will terminate after line b'addcon thread start'
    2026-08-14T11:05:59.268236Z TestFramework (INFO): Starting node and will terminate after line b'initload thread start'
    2026-08-14T11:05:59.445965Z TestFramework (INFO): Starting node and will terminate after line b'txidx thread start'
    2026-08-14T11:05:59.673355Z TestFramework (INFO): Starting node and will terminate after line b'blkfltbscidx thread start'
    2026-08-14T11:05:59.901937Z TestFramework (INFO): Starting node and will terminate after line b'coinstatsidx thread start'
    2026-08-14T11:06:00.129613Z TestFramework (INFO): Starting node and will terminate after line b'txospenderidx thread start'
    2026-08-14T11:06:00.357869Z TestFramework (INFO): Starting node and will terminate after line b'msghand thread start'
    2026-08-14T11:06:00.584700Z TestFramework (INFO): Starting node and will terminate after line b'net thread start'
    2026-08-14T11:06:00.813072Z TestFramework (INFO): Starting node and will terminate after line b'addcon thread start'
    2026-08-14T11:06:00.991162Z TestFramework (INFO): Starting node and will terminate after line b'Verifying wallet'
    2026-08-14T11:06:01.153547Z TestFramework (INFO): Starting node and will terminate after line b'Reindexing block file blk00000.dat'
    2026-08-14T11:06:01.721225Z TestFramework (INFO): Test startup errors after removing certain essential files
    2026-08-14T11:06:01.721454Z TestFramework (INFO): Deleting file to ensure failure /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node0/regtest/blocks/index/000005.ldb
    2026-08-14T11:06:02.289948Z TestFramework (INFO): Deleting file to ensure failure /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node0/regtest/chainstate/000008.ldb
    2026-08-14T11:06:02.290023Z TestFramework (INFO): Deleting file to ensure failure /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node0/regtest/chainstate/000005.ldb
    2026-08-14T11:06:02.858514Z TestFramework (INFO): Deleting file to ensure failure /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node0/regtest/blocks/blk00000.dat
    2026-08-14T11:06:03.426826Z TestFramework (INFO): Deleting file to ensure failure /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node0/regtest/indexes/txindex/MANIFEST-000012
    2026-08-14T11:06:03.994953Z TestFramework (INFO): Deleting file to ensure failure /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node0/regtest/indexes/txospenderindex/db/MANIFEST-000017
    2026-08-14T11:06:04.563143Z TestFramework (INFO): Test startup errors after perturbing certain essential files
    2026-08-14T11:06:04.568540Z TestFramework (INFO): Perturbing file to ensure failure /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node0/regtest/blocks/index/000033.ldb
    2026-08-14T11:06:04.741147Z TestFramework (INFO): Perturbing file to ensure failure /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node0/regtest/chainstate/000005.ldb
    2026-08-14T11:06:04.741239Z TestFramework (INFO): Perturbing file to ensure failure /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node0/regtest/chainstate/000019.ldb
    2026-08-14T11:06:04.741283Z TestFramework (INFO): Perturbing file to ensure failure /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node0/regtest/chainstate/000021.ldb
    2026-08-14T11:06:04.741319Z TestFramework (INFO): Perturbing file to ensure failure /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node0/regtest/chainstate/000024.ldb
    2026-08-14T11:06:04.741355Z TestFramework (INFO): Perturbing file to ensure failure /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node0/regtest/chainstate/000027.ldb
    2026-08-14T11:06:04.914541Z TestFramework (INFO): Perturbing file to ensure failure /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node0/regtest/blocks/blk00000.dat
    2026-08-14T11:06:05.087296Z TestFramework (INFO): Perturbing file to ensure failure /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node0/regtest/indexes/blockfilter/basic/db/000018.ldb
    2026-08-14T11:06:05.087390Z TestFramework (INFO): Perturbing file to ensure failure /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node0/regtest/indexes/blockfilter/basic/db/000020.ldb
    2026-08-14T11:06:05.087435Z TestFramework (INFO): Perturbing file to ensure failure /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node0/regtest/indexes/blockfilter/basic/db/000021.log
    2026-08-14T11:06:05.260246Z TestFramework (INFO): Perturbing file to ensure failure /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node0/regtest/indexes/coinstatsindex/db/000021.log
    2026-08-14T11:06:05.260336Z TestFramework (INFO): Perturbing file to ensure failure /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node0/regtest/indexes/coinstatsindex/db/000020.ldb
    2026-08-14T11:06:05.260377Z TestFramework (INFO): Perturbing file to ensure failure /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node0/regtest/indexes/coinstatsindex/db/000018.ldb
    2026-08-14T11:06:05.432832Z TestFramework (INFO): Perturbing file to ensure failure /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node0/regtest/indexes/txindex/000021.log
    2026-08-14T11:06:05.605645Z TestFramework (INFO): Perturbing file to ensure failure /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node0/regtest/indexes/txindex/CURRENT
    2026-08-14T11:06:05.778547Z TestFramework (INFO): Perturbing file to ensure failure /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node0/regtest/indexes/txospenderindex/db/LOCK
    2026-08-14T11:06:05.778639Z TestFramework (INFO): Perturbing file to ensure failure /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node0/regtest/indexes/txospenderindex/db/000005.ldb
    2026-08-14T11:06:05.778684Z TestFramework (INFO): Perturbing file to ensure failure /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node0/regtest/indexes/txospenderindex/db/000016.ldb
    2026-08-14T11:06:05.778722Z TestFramework (INFO): Perturbing file to ensure failure /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node0/regtest/indexes/txospenderindex/db/000018.ldb
    2026-08-14T11:06:05.778760Z TestFramework (INFO): Perturbing file to ensure failure /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node0/regtest/indexes/txospenderindex/db/000021.ldb
    2026-08-14T11:06:05.778797Z TestFramework (INFO): Perturbing file to ensure failure /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node0/regtest/indexes/txospenderindex/db/000022.log
    2026-08-14T11:06:05.778851Z TestFramework (INFO): Perturbing file to ensure failure /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node0/regtest/indexes/txospenderindex/db/MANIFEST-000020
    2026-08-14T11:06:05.778910Z TestFramework (INFO): Perturbing file to ensure failure /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node0/regtest/indexes/txospenderindex/db/CURRENT
    2026-08-14T11:06:05.944733Z TestFramework (INFO): Testing waitforblockheight RPC call followed by break signal
    2026-08-14T11:06:06.305172Z TestFramework (INFO): Test that stopping and restarting a node that has done nothing is not causing a failure
    2026-08-14T11:06:07.111608Z TestFramework (INFO): Testing node startup with RLIM_INFINITY fd limit
    2026-08-14T11:06:07.111670Z TestFramework (INFO): Skipping rlimit test: cannot set soft limit (hard=524288)
    2026-08-14T11:06:07.111700Z TestFramework (INFO): Testing node startup with fd limit above INT_MAX
    2026-08-14T11:06:07.111725Z TestFramework (INFO): Skipping rlimit test: cannot set soft limit (hard=524288)
    2026-08-14T11:06:07.262595Z TestFramework (INFO): Checking -rpcmaxconnections setting that would overflow int is rejected
    2026-08-14T11:06:07.266663Z TestFramework (INFO): Checking that large -maxconnections setting gets adjusted for available file descriptors
    2026-08-14T11:06:07.518734Z TestFramework (ERROR): Unexpected exception:
    Traceback (most recent call last):
      File "/home/will/src/core/bitcoin/worktrees/pr-35730/test/functional/test_framework/test_framework.py", line 145, in main
        self.run_test()
        ~~~~~~~~~~~~~^^
      File "/home/will/src/core/bitcoin/worktrees/pr-35730/build/test/functional/feature_init.py", line 420, in run_test
        self.init_fd_overflow_test()
        ~~~~~~~~~~~~~~~~~~~~~~~~~~^^
      File "/home/will/src/core/bitcoin/worktrees/pr-35730/build/test/functional/feature_init.py", line 388, in init_fd_overflow_test
        with node.assert_debug_log(expected_msgs=[f"Reducing -maxconnections from {soft} "]):
             ~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/nix/store/3n4qphl9s728sz8frmpqqrv9b1m87g68-python3-3.14.7/lib/python3.14/contextlib.py", line 148, in __exit__
        next(self.gen)
        ~~~~^^^^^^^^^^
      File "/home/will/src/core/bitcoin/worktrees/pr-35730/test/functional/test_framework/test_node.py", line 632, in assert_debug_log
        self._raise_assertion_error(f'Expected message(s) {remaining_expected!s} '
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                                    f'not found in log:\n\n{join_log(log)}\n\n')
                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/home/will/src/core/bitcoin/worktrees/pr-35730/test/functional/test_framework/test_node.py", line 228, in _raise_assertion_error
        raise AssertionError(self._node_msg(msg))
    AssertionError: [node 1] Expected message(s) ['Reducing -maxconnections from 1024 '] not found in log:
    
     -
     -
     -
     -
     -
     - 2026-08-14T11:06:07.269771Z [init] [../src/init/common.cpp:156] [LogPackageVersion] Bitcoin Core version v31.99.0-1f985995afe7 (release build)
     - 2026-08-14T11:06:07.269774Z [init] [../src/init.cpp:809] [InitParameterInteraction] parameter interaction: -bind set -> setting -listen=1
     - 2026-08-14T11:06:07.269791Z [init] [../src/init/common.cpp:102] [SetLoggingCategories] Log output may contain privacy-sensitive information. Be cautious when sharing logs.
     - 2026-08-14T11:06:07.269808Z [init] [../src/kernel/context.cpp:20] [operator()] Using the 'x86_shani(1way;2way)' SHA256 implementation
     - 2026-08-14T11:06:07.370910Z [init] [../src/random.cpp:110] [ReportHardwareRand] Using RdSeed as an additional entropy source
     - 2026-08-14T11:06:07.370913Z [init] [../src/random.cpp:113] [ReportHardwareRand] Using RdRand as an additional entropy source
     - 2026-08-14T11:06:07.372318Z [init] [../src/init/common.cpp:124] [StartLogging] Default data directory /home/will/.bitcoin
     - 2026-08-14T11:06:07.372321Z [init] [../src/init/common.cpp:125] [StartLogging] Using data directory /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node1/regtest
     - 2026-08-14T11:06:07.372326Z [init] [../src/init/common.cpp:134] [StartLogging] Config file: /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node1/bitcoin.conf
     - 2026-08-14T11:06:07.372332Z [init] [../src/common/args.cpp:994] [logArgsPrefix] Config file arg: regtest="1"
     - 2026-08-14T11:06:07.372335Z [init] [../src/common/args.cpp:994] [logArgsPrefix] Config file arg: [regtest] bind="127.0.0.1"
     - 2026-08-14T11:06:07.372337Z [init] [../src/common/args.cpp:994] [logArgsPrefix] Config file arg: [regtest] connect="0"
     - 2026-08-14T11:06:07.372339Z [init] [../src/common/args.cpp:994] [logArgsPrefix] Config file arg: [regtest] discover="0"
     - 2026-08-14T11:06:07.372341Z [init] [../src/common/args.cpp:994] [logArgsPrefix] Config file arg: [regtest] dnsseed="0"
     - 2026-08-14T11:06:07.372343Z [init] [../src/common/args.cpp:994] [logArgsPrefix] Config file arg: [regtest] fallbackfee="0.0002"
     - 2026-08-14T11:06:07.372345Z [init] [../src/common/args.cpp:994] [logArgsPrefix] Config file arg: [regtest] fixedseeds="0"
     - 2026-08-14T11:06:07.372347Z [init] [../src/common/args.cpp:994] [logArgsPrefix] Config file arg: [regtest] keypool="1"
     - 2026-08-14T11:06:07.372348Z [init] [../src/common/args.cpp:994] [logArgsPrefix] Config file arg: [regtest] listenonion="0"
     - 2026-08-14T11:06:07.372350Z [init] [../src/common/args.cpp:994] [logArgsPrefix] Config file arg: [regtest] maxconnections="94"
     - 2026-08-14T11:06:07.372352Z [init] [../src/common/args.cpp:994] [logArgsPrefix] Config file arg: [regtest] natpmp="0"
     - 2026-08-14T11:06:07.372354Z [init] [../src/common/args.cpp:994] [logArgsPrefix] Config file arg: [regtest] par="2"
     - 2026-08-14T11:06:07.372355Z [init] [../src/common/args.cpp:994] [logArgsPrefix] Config file arg: [regtest] peertimeout="999999999"
     - 2026-08-14T11:06:07.372357Z [init] [../src/common/args.cpp:994] [logArgsPrefix] Config file arg: [regtest] port="11001"
     - 2026-08-14T11:06:07.372359Z [init] [../src/common/args.cpp:994] [logArgsPrefix] Config file arg: [regtest] prevoutfetchthreads="1"
     - 2026-08-14T11:06:07.372361Z [init] [../src/common/args.cpp:994] [logArgsPrefix] Config file arg: [regtest] printtoconsole="0"
     - 2026-08-14T11:06:07.372364Z [init] [../src/common/args.cpp:994] [logArgsPrefix] Config file arg: [regtest] rpcdoccheck="1"
     - 2026-08-14T11:06:07.372366Z [init] [../src/common/args.cpp:994] [logArgsPrefix] Config file arg: [regtest] rpcport="16001"
     - 2026-08-14T11:06:07.372368Z [init] [../src/common/args.cpp:994] [logArgsPrefix] Config file arg: [regtest] rpcservertimeout="99000"
     - 2026-08-14T11:06:07.372370Z [init] [../src/common/args.cpp:994] [logArgsPrefix] Config file arg: [regtest] rpcthreads="2"
     - 2026-08-14T11:06:07.372372Z [init] [../src/common/args.cpp:994] [logArgsPrefix] Config file arg: [regtest] server="1"
     - 2026-08-14T11:06:07.372373Z [init] [../src/common/args.cpp:994] [logArgsPrefix] Config file arg: [regtest] shrinkdebugfile="0"
     - 2026-08-14T11:06:07.372376Z [init] [../src/common/args.cpp:994] [logArgsPrefix] Config file arg: [regtest] unsafesqlitesync="1"
     - 2026-08-14T11:06:07.372378Z [init] [../src/common/args.cpp:994] [logArgsPrefix] Command-line arg: datadir="/mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node1"
     - 2026-08-14T11:06:07.372380Z [init] [../src/common/args.cpp:994] [logArgsPrefix] Command-line arg: debug=""
     - 2026-08-14T11:06:07.372382Z [init] [../src/common/args.cpp:994] [logArgsPrefix] Command-line arg: debugexclude="leveldb"
     - 2026-08-14T11:06:07.372384Z [init] [../src/common/args.cpp:994] [logArgsPrefix] Command-line arg: debugexclude="rand"
     - 2026-08-14T11:06:07.372386Z [init] [../src/common/args.cpp:994] [logArgsPrefix] Command-line arg: loglevel="trace"
     - 2026-08-14T11:06:07.372389Z [init] [../src/common/args.cpp:994] [logArgsPrefix] Command-line arg: logratelimit=false
     - 2026-08-14T11:06:07.372391Z [init] [../src/common/args.cpp:994] [logArgsPrefix] Command-line arg: logsourcelocations=""
     - 2026-08-14T11:06:07.372393Z [init] [../src/common/args.cpp:994] [logArgsPrefix] Command-line arg: logthreadnames=""
     - 2026-08-14T11:06:07.372395Z [init] [../src/common/args.cpp:994] [logArgsPrefix] Command-line arg: logtimemicros=""
     - 2026-08-14T11:06:07.372396Z [init] [../src/common/args.cpp:994] [logArgsPrefix] Command-line arg: maxconnections="1024"
     - 2026-08-14T11:06:07.372398Z [init] [../src/common/args.cpp:994] [logArgsPrefix] Command-line arg: uacomment="testnode1"
     - 2026-08-14T11:06:07.372400Z [init] [../src/common/args.cpp:994] [logArgsPrefix] Command-line arg: v2transport="0"
     - 2026-08-14T11:06:07.372402Z [init] [../src/init.cpp:1519] [AppInitMain] Using at most 1024 automatic connections (1202 file descriptors available)
     - 2026-08-14T11:06:07.372456Z [init] [../src/init.cpp:1559] [AppInitMain] Log rate limiting disabled
     - 2026-08-14T11:06:07.372521Z [scheduler] [../src/util/thread.cpp:19] [TraceThread] scheduler thread start
     - 2026-08-14T11:06:07.375114Z [init] [../src/httpserver.cpp:113] [InitHTTPAllowList] [http] Allowing HTTP connections from: 127.0.0.0/8 ::1/128
     - 2026-08-14T11:06:07.375124Z [init] [../src/httpserver.cpp:1250] [InitHTTPServer] Binding RPC on address ::1 port 16001
     - 2026-08-14T11:06:07.375143Z [init] [../src/httpserver.cpp:1250] [InitHTTPServer] Binding RPC on address 127.0.0.1 port 16001
     - 2026-08-14T11:06:07.375152Z [init] [../src/httpserver.cpp:1272] [InitHTTPServer] [http] Initialized HTTP server
     - 2026-08-14T11:06:07.375156Z [init] [../src/httpserver.cpp:1275] [InitHTTPServer] [http] set work queue of depth 64
     - 2026-08-14T11:06:07.375158Z [init] [../src/rpc/server.cpp:640] [StartRPC] [rpc] Starting RPC
     - 2026-08-14T11:06:07.375160Z [init] [../src/httprpc.cpp:342] [StartHTTPRPC] [rpc] Starting HTTP RPC server
     - 2026-08-14T11:06:07.375188Z [init] [../src/rpc/request.cpp:140] [GenerateAuthCookie] Generated RPC authentication cookie /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node1/regtest/.cookie
     - 2026-08-14T11:06:07.375192Z [init] [../src/rpc/request.cpp:141] [GenerateAuthCookie] Permissions used for cookie: rw-------
     - 2026-08-14T11:06:07.375195Z [init] [../src/httprpc.cpp:274] [InitRPCAuthentication] Using random cookie authentication.
     - 2026-08-14T11:06:07.375202Z [init] [../src/httpserver.cpp:245] [RegisterHTTPHandler] [http] Registering HTTP handler for / (exactmatch 1)
     - 2026-08-14T11:06:07.375205Z [init] [../src/httpserver.cpp:245] [RegisterHTTPHandler] [http] Registering HTTP handler for /wallet/ (exactmatch 0)
     - 2026-08-14T11:06:07.375208Z [init] [../src/httpserver.cpp:1283] [StartHTTPServer] Starting HTTP server with 2 worker threads
     - 2026-08-14T11:06:07.375264Z [init] [../src/wallet/load.cpp:53] [VerifyWallets] Using wallet directory /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node1/regtest/wallets
     - 2026-08-14T11:06:07.375273Z [init] [../src/wallet/walletdb.cpp:70] [LogDBInfo] Using SQLite Version 3.53.3
     - 2026-08-14T11:06:07.375276Z [init] [../src/noui.cpp:54] [noui_InitMessage] init message: Verifying wallet(s)…
     - 2026-08-14T11:06:07.375283Z [http.01] [../src/util/thread.cpp:19] [TraceThread] http.01 thread start
     - 2026-08-14T11:06:07.375305Z [init] [../src/init.cpp:1709] [AppInitMain] Using /16 prefix for IP bucketing
     - 2026-08-14T11:06:07.375308Z [init] [../src/noui.cpp:54] [noui_InitMessage] init message: Loading P2P addresses…
     - 2026-08-14T11:06:07.375311Z [http.00] [../src/util/thread.cpp:19] [TraceThread] http.00 thread start
     - 2026-08-14T11:06:07.375334Z [http] [../src/util/thread.cpp:19] [TraceThread] http thread start
     - 2026-08-14T11:06:07.375517Z [init] [../src/logging/timer.h:58] [Log] [addrman] CheckAddrman: new 0, tried 0, total 0 started
     - 2026-08-14T11:06:07.375569Z [init] [../src/logging/timer.h:58] [Log] [addrman] CheckAddrman: completed (0.05ms)
     - 2026-08-14T11:06:07.375577Z [init] [../src/addrdb.cpp:207] [LoadAddrman] Loaded 0 addresses from peers.dat 0ms
     - 2026-08-14T11:06:07.375736Z [init] [../src/noui.cpp:54] [noui_InitMessage] init message: Loading banlist…
     - 2026-08-14T11:06:07.375755Z [init] [../src/banman.cpp:39] [LoadBanlist] [net] Loaded 0 banned node addresses/subnets 0ms
     - 2026-08-14T11:06:07.375762Z [init] [../src/net.cpp:2476] [SetTryNewOutboundPeer] [net] setting try another outbound peer=false
     - 2026-08-14T11:06:07.375768Z [init] [../src/net.cpp:3454] [SetNetworkActive] SetNetworkActive: true
     - 2026-08-14T11:06:07.376634Z [init] [../src/policy/fees/block_policy_estimator.cpp:473] [Read] [estimatefee] Reading estimates: 237 buckets counting confirms up to 48 blocks
     - 2026-08-14T11:06:07.376750Z [init] [../src/policy/fees/block_policy_estimator.cpp:473] [Read] [estimatefee] Reading estimates: 237 buckets counting confirms up to 12 blocks
     - 2026-08-14T11:06:07.377154Z [init] [../src/policy/fees/block_policy_estimator.cpp:473] [Read] [estimatefee] Reading estimates: 237 buckets counting confirms up to 1008 blocks
     - 2026-08-14T11:06:07.377232Z [init] [../src/init.cpp:1911] [AppInitMain] Cache configuration:
     - 2026-08-14T11:06:07.377248Z [init] [../src/init.cpp:1912] [AppInitMain] * Using 2.0 MiB for block index database
     - 2026-08-14T11:06:07.377250Z [init] [../src/init.cpp:1923] [AppInitMain] * Using 8.0 MiB for chain state database
     - 2026-08-14T11:06:07.377268Z [init] [../src/init.cpp:1404] [InitAndLoadChainstate] * Using 1014.0 MiB for in-memory UTXO set (plus up to 286.1 MiB of unused mempool space)
     - 2026-08-14T11:06:07.377277Z [init] [../src/checkqueue.h:148] [CCheckQueue] Script verification uses 1 additional threads
     - 2026-08-14T11:06:07.377340Z [init] [../src/node/blockstorage.cpp:1235] [InitBlocksdirXorKey] Using obfuscation key for blocksdir *.dat files (/mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node1/regtest/blocks): '213253b84bc03896'
     - 2026-08-14T11:06:07.377351Z [init] [../src/dbwrapper.cpp:247] [CDBWrapper] Opening LevelDB in /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node1/regtest/blocks/index
     - 2026-08-14T11:06:07.377466Z [init] [../src/dbwrapper.cpp:255] [CDBWrapper] Opened LevelDB successfully
     - 2026-08-14T11:06:07.377471Z [init] [../src/dbwrapper.cpp:271] [CDBWrapper] Using obfuscation key for /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node1/regtest/blocks/index: 0000000000000000
     - 2026-08-14T11:06:07.382846Z [init] [../src/script/sigcache.cpp:35] [SignatureCache] Using 16 MiB out of 16 MiB requested for signature cache, able to store 524288 elements
     - 2026-08-14T11:06:07.388066Z [init] [../src/validation.cpp:2048] [ValidationCache] Using 16 MiB out of 16 MiB requested for script execution cache, able to store 524288 elements
     - 2026-08-14T11:06:07.388078Z [init] [../src/noui.cpp:54] [noui_InitMessage] init message: Loading block index…
     - 2026-08-14T11:06:07.388082Z [init] [../src/node/chainstate.cpp:157] [LoadChainstate] Validating signatures for all blocks.
     - 2026-08-14T11:06:07.388084Z [init] [../src/node/chainstate.cpp:159] [LoadChainstate] Setting nMinimumChainWork=0000000000000000000000000000000000000000000000000000000000000000
     - 2026-08-14T11:06:07.388170Z [init] [../src/node/blockstorage.cpp:559] [LoadBlockIndexDB] Loading block index db: last block file = 0
     - 2026-08-14T11:06:07.388177Z [init] [../src/node/blockstorage.cpp:563] [LoadBlockIndexDB] Loading block index db: last block file info: CBlockFileInfo(blocks=1, size=293, heights=0...0, time=2011-02-02...2011-02-02)
     - 2026-08-14T11:06:07.388180Z [init] [../src/node/blockstorage.cpp:574] [LoadBlockIndexDB] Checking all blk files are present...
     - 2026-08-14T11:06:07.388195Z [init] [../src/node/chainstate.cpp:87] [CompleteChainstateInitialization] Initializing chainstate Chainstate [ibd] @ height -1 (null)
     - 2026-08-14T11:06:07.388202Z [init] [../src/dbwrapper.cpp:247] [CDBWrapper] Opening LevelDB in /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node1/regtest/chainstate
     - 2026-08-14T11:06:07.388297Z [init] [../src/dbwrapper.cpp:255] [CDBWrapper] Opened LevelDB successfully
     - 2026-08-14T11:06:07.388313Z [init] [../src/dbwrapper.cpp:271] [CDBWrapper] Using obfuscation key for /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/node1/regtest/chainstate: 9bab65d674118747
     - 2026-08-14T11:06:07.388370Z [init] [../src/validation.cpp:1868] [InitCache] Block input prevout fetching uses 1 additional threads
     - 2026-08-14T11:06:07.388383Z [init] [../src/validation.cpp:4602] [LoadChainTip] Loaded best chain: hashBestChain=0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206 height=0 date=2011-02-02T23:16:42Z progress=0.000002
     - 2026-08-14T11:06:07.388384Z [prevout.00] [../src/util/thread.cpp:19] [TraceThread] prevout.00 thread start
     - 2026-08-14T11:06:07.388387Z [init] [../src/noui.cpp:54] [noui_InitMessage] init message: Verifying blocks…
     - 2026-08-14T11:06:07.388398Z [init] [../src/init.cpp:1492] [InitAndLoadChainstate] Block index and chainstate loaded
     - 2026-08-14T11:06:07.388417Z [init] [../src/init.cpp:2027] [AppInitMain] Setting NODE_NETWORK in non-prune mode
     - 2026-08-14T11:06:07.388442Z [init] [../src/init.cpp:2165] [AppInitMain] block tree size = 1
     - 2026-08-14T11:06:07.388445Z [init] [../src/init.cpp:2178] [AppInitMain] nBestHeight = 0
     - 2026-08-14T11:06:07.388489Z [init] [../src/net.cpp:3426] [BindListenPort] Bound to 127.0.0.1:11001
     - 2026-08-14T11:06:07.388494Z [init] [../src/noui.cpp:54] [noui_InitMessage] init message: Starting network threads…
     - 2026-08-14T11:06:07.388510Z [initload] [../src/util/thread.cpp:19] [TraceThread] initload thread start
     - 2026-08-14T11:06:07.388519Z [init] [../src/net.cpp:3624] [Start] DNS seeding disabled
     - 2026-08-14T11:06:07.388543Z [initload] [../src/node/mempool_persist.cpp:77] [LoadMempool] Loading 0 mempool transactions from file...
     - 2026-08-14T11:06:07.388547Z [initload] [../src/node/mempool_persist.cpp:149] [LoadMempool] Imported mempool transactions from file: 0 succeeded, 0 failed, 0 expired, 0 already there, 0 waiting for initial broadcast
     - 2026-08-14T11:06:07.388550Z [init] [../src/noui.cpp:54] [noui_InitMessage] init message: Done loading
     - 2026-08-14T11:06:07.388556Z [addcon] [../src/util/thread.cpp:19] [TraceThread] addcon thread start
     - 2026-08-14T11:06:07.388551Z [initload] [../src/util/thread.cpp:21] [TraceThread] initload thread exit
     - 2026-08-14T11:06:07.388604Z [msghand] [../src/util/thread.cpp:19] [TraceThread] msghand thread start
     - 2026-08-14T11:06:07.388608Z [net] [../src/util/thread.cpp:19] [TraceThread] net thread start
     - 2026-08-14T11:06:07.517950Z [http] [../src/httpserver.cpp:845] [NewSockAccepted] [http] HTTP Connection accepted from 127.0.0.1:41322 (id=0)
     - 2026-08-14T11:06:07.517978Z [http] [../src/httpserver.cpp:1040] [MaybeDispatchRequestsFromClient] [http] Received a POST request for / from 127.0.0.1:41322 (id=0)
     - 2026-08-14T11:06:07.518044Z [http.01] [../src/rpc/request.cpp:241] [parse] [rpc] ThreadRPCServer method=getblockcount user=__cookie__ id=75
     - 2026-08-14T11:06:07.518076Z [http.01] [../src/httpserver.cpp:612] [WriteReply] [http] HTTPResponse (status code: 200 size: 145) added to send buffer for client 127.0.0.1:41322 (id=0)
     - 2026-08-14T11:06:07.518100Z [http.01] [../src/httpserver.cpp:1201] [MaybeSendBytesFromBuffer] [http] Sent 145 bytes to client 127.0.0.1:41322 (id=0)
     - 2026-08-14T11:06:07.518308Z [http] [../src/httpserver.cpp:1040] [MaybeDispatchRequestsFromClient] [http] Received a POST request for / from 127.0.0.1:41322 (id=0)
     - 2026-08-14T11:06:07.518361Z [http.00] [../src/rpc/request.cpp:241] [parse] [rpc] ThreadRPCServer method=getmempoolinfo user=__cookie__ id=76
     - 2026-08-14T11:06:07.518417Z [http.00] [../src/httpserver.cpp:612] [WriteReply] [http] HTTPResponse (status code: 200 size: 461) added to send buffer for client 127.0.0.1:41322 (id=0)
     - 2026-08-14T11:06:07.518437Z [http.00] [../src/httpserver.cpp:1201] [MaybeSendBytesFromBuffer] [http] Sent 461 bytes to client 127.0.0.1:41322 (id=0)
    
    
    2026-08-14T11:06:07.570299Z TestFramework (INFO): Not stopping nodes as test failed. The dangling processes will be cleaned up later.
    2026-08-14T11:06:07.570403Z TestFramework (WARNING): Not cleaning up dir /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0
    2026-08-14T11:06:07.570441Z TestFramework (ERROR): Test failed. Test logging available at /mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0/test_framework.log
    2026-08-14T11:06:07.570513Z TestFramework (ERROR):
    2026-08-14T11:06:07.570575Z TestFramework (ERROR): Hint: Call /home/will/src/core/bitcoin/worktrees/pr-35730/test/functional/combine_logs.py '/mnt/tmp/test_runner_β‚Ώ_πŸƒ_20260814_120555/feature_init_0' to consolidate all logs
    2026-08-14T11:06:07.570604Z TestFramework (ERROR):
    2026-08-14T11:06:07.570626Z TestFramework (ERROR): If this failure happened unexpectedly or intermittently, please file a bug and provide a link or upload of the combined log.
    2026-08-14T11:06:07.570661Z TestFramework (ERROR): https://github.com/bitcoin/bitcoin/issues
    2026-08-14T11:06:07.570686Z TestFramework (ERROR):
    
    
    stderr:
    [node 1] Cleaning up leftover process
    
    
    
    TEST            | STATUS    | DURATION
    
    feature_init.py | βœ– Failed  | 12 s
    
    ALL             | βœ– Failed  | 12 s (accumulated)
    Runtime: 12 s
    

    </details>

    I think this is because of my ulimit settings?

    ❯ ulimit -Sn
      ulimit -Hn
    1024
    524288
    
  63. DrahtBot requested review from willcl-ark on Aug 14, 2026

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-08-14 17:51 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me