However, when an empty batch is executed, some RPC endpoint (mine is Erigon) returns an object specifying the error instead of an array. This breaks the parsing and result in error: AttributeError: 'str' object has no attribute 'get'
The same happens when the batch size exceeds the limit:
{"jsonrpc":"2.0","id":null,"error":{"code":-32000,"message":"batch limit 100 exceeded (can increase by --rpc.batch.limit). Requested batch of size: 526"}}
Code that produced the error
from web3 import Web3, HTTPProvider
w3 = Web3(HTTPProvider("http://localhost:8545"))
with w3.batch_requests() as batch:
batch.execute()
Full error output
AttributeError Traceback (most recent call last)
Cell In[222], line 5
3 with w3.batch_requests() as batch:
----> 4 batch.execute()
File ~/.cache/pypoetry/virtualenvs/etherspect-dagster-jFkAMd9f-py3.12/lib/python3.12/site-packages/web3/_utils/batching.py:147, in RequestBatcher.execute(self)
145 def execute(self) -> List["RPCResponse"]:
146 self._validate_is_batching()
--> 147 responses = self.web3.manager._make_batch_request(self._requests_info)
148 self._end_batching()
149 return responses
File ~/.cache/pypoetry/virtualenvs/etherspect-dagster-jFkAMd9f-py3.12/lib/python3.12/site-packages/web3/manager.py:430, in RequestManager._make_batch_request(self, requests_info)
426 provider = cast(JSONBaseProvider, self.provider)
427 request_func = provider.batch_request_func(
428 cast("Web3", self.w3), cast("MiddlewareOnion", self.middleware_onion)
429 )
--> 430 responses = request_func(
431 [
432 (method, params)
433 for (method, params), _response_formatters in requests_info
434 ]
435 )
436 formatted_responses = [
437 self._format_batched_response(info, resp)
438 for info, resp in zip(requests_info, responses)
439 ]
440 return list(formatted_responses)
File ~/.cache/pypoetry/virtualenvs/etherspect-dagster-jFkAMd9f-py3.12/lib/python3.12/site-packages/web3/middleware/base.py:70, in Web3Middleware.wrap_make_batch_request.<locals>.middleware(requests_info)
63 def middleware(
64 requests_info: List[Tuple["RPCEndpoint", Any]]
65 ) -> List["RPCResponse"]:
66 req_processed = [
67 self.request_processor(method, params)
68 for (method, params) in requests_info
69 ]
---> 70 responses = make_batch_request(req_processed)
71 methods, _params = zip(*req_processed)
72 formatted_responses = [
73 self.response_processor(m, r) for m, r in zip(methods, responses)
74 ]
File ~/.cache/pypoetry/virtualenvs/etherspect-dagster-jFkAMd9f-py3.12/lib/python3.12/site-packages/web3/middleware/base.py:70, in Web3Middleware.wrap_make_batch_request.<locals>.middleware(requests_info)
63 def middleware(
64 requests_info: List[Tuple["RPCEndpoint", Any]]
65 ) -> List["RPCResponse"]:
66 req_processed = [
67 self.request_processor(method, params)
68 for (method, params) in requests_info
69 ]
---> 70 responses = make_batch_request(req_processed)
71 methods, _params = zip(*req_processed)
72 formatted_responses = [
73 self.response_processor(m, r) for m, r in zip(methods, responses)
74 ]
[... skipping similar frames: Web3Middleware.wrap_make_batch_request.<locals>.middleware at line 70 (2 times)]
File ~/.cache/pypoetry/virtualenvs/etherspect-dagster-jFkAMd9f-py3.12/lib/python3.12/site-packages/web3/middleware/base.py:70, in Web3Middleware.wrap_make_batch_request.<locals>.middleware(requests_info)
63 def middleware(
64 requests_info: List[Tuple["RPCEndpoint", Any]]
65 ) -> List["RPCResponse"]:
66 req_processed = [
67 self.request_processor(method, params)
68 for (method, params) in requests_info
69 ]
---> 70 responses = make_batch_request(req_processed)
71 methods, _params = zip(*req_processed)
72 formatted_responses = [
73 self.response_processor(m, r) for m, r in zip(methods, responses)
74 ]
File ~/.cache/pypoetry/virtualenvs/etherspect-dagster-jFkAMd9f-py3.12/lib/python3.12/site-packages/web3/providers/rpc/rpc.py:188, in HTTPProvider.make_batch_request(self, batch_requests)
186 self.logger.debug("Received batch response HTTP.")
187 responses_list = cast(List[RPCResponse], self.decode_rpc_response(raw_response))
--> 188 return sort_batch_response_by_response_ids(responses_list)
File ~/.cache/pypoetry/virtualenvs/etherspect-dagster-jFkAMd9f-py3.12/lib/python3.12/site-packages/web3/_utils/batching.py:203, in sort_batch_response_by_response_ids(responses)
200 def sort_batch_response_by_response_ids(
201 responses: List["RPCResponse"],
202 ) -> List["RPCResponse"]:
--> 203 if all(response.get("id") is not None for response in responses):
204 # If all responses have an `id`, sort them by `id, since the JSON-RPC 2.0 spec
205 # doesn't guarantee order.
206 return sorted(responses, key=lambda response: response["id"])
207 else:
208 # If any response is missing an `id`, which should only happen on particular
209 # errors, return them in the order they were received and hope that the
210 # provider is returning them in order. Issue a warning.
File ~/.cache/pypoetry/virtualenvs/etherspect-dagster-jFkAMd9f-py3.12/lib/python3.12/site-packages/web3/_utils/batching.py:203, in <genexpr>(.0)
200 def sort_batch_response_by_response_ids(
201 responses: List["RPCResponse"],
202 ) -> List["RPCResponse"]:
--> 203 if all(response.get("id") is not None for response in responses):
204 # If all responses have an `id`, sort them by `id, since the JSON-RPC 2.0 spec
205 # doesn't guarantee order.
206 return sorted(responses, key=lambda response: response["id"])
207 else:
208 # If any response is missing an `id`, which should only happen on particular
209 # errors, return them in the order they were received and hope that the
210 # provider is returning them in order. Issue a warning.
AttributeError: 'str' object has no attribute 'get'
Fill this section in if you know how this could or should be fixed
Revise the response parsing behavior in web3/providers/rpc/rpc.py:make_batch_request() and any other relevant files
What happened?
A batch request to an Ethereum RPC endpoint is responded with an array containing the responses to each request in the batch:
So Web3.py assumes the response is an array while parsing it: https://github.com/ethereum/web3.py/blob/3c412f81d19f70d70b262ae8fefe6bd8411667e3/web3/providers/rpc/rpc.py#L187-L188
However, when an empty batch is executed, some RPC endpoint (mine is Erigon) returns an object specifying the error instead of an array. This breaks the parsing and result in error:
AttributeError: 'str' object has no attribute 'get'
The same happens when the batch size exceeds the limit:
Code that produced the error
Full error output
Fill this section in if you know how this could or should be fixed
Revise the response parsing behavior in
web3/providers/rpc/rpc.py:make_batch_request()
and any other relevant filesweb3 Version
7.4.0
Python Version
3.12.5
Operating System
linux
Output from
pip freeze