APIClient.push() with no auth config results in EOF with Docker 28.3.3
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 7.2k
- Forks
- 1.7k
- Avg merge
- 13d 8h
- Merged PRs (30d)
- 2
Description
I reported this to moby/moby (https://github.com/moby/moby/issues/50614), but since it also affects Docker SDK for Python, I'd like to report it here as well.
Reproducer from https://github.com/moby/moby/issues/50614#issuecomment-3148412359:
-
Run on shell:
docker run --name registry --publish 5000:5000 --detach registry:2.8.3 docker pull hello-world:latest docker tag hello-world:latest localhost:5000/hello-world:latest -
Run in Python, with Docker SDK for Python available:
import docker list(docker.APIClient().push("localhost:5000/hello-world:latest", stream=True, decode=True))
This results in:
Traceback (most recent call last):
File "/usr/lib/python3.13/http/client.py", line 579, in _get_chunk_left
chunk_left = self._read_next_chunk_size()
File "/usr/lib/python3.13/http/client.py", line 546, in _read_next_chunk_size
return int(line, 16)
ValueError: invalid literal for int() with base 16: b''
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/lib/python3.13/http/client.py", line 597, in _read_chunked
while (chunk_left := self._get_chunk_left()) is not None:
~~~~~~~~~~~~~~~~~~~~^^
File "/usr/lib/python3.13/http/client.py", line 581, in _get_chunk_left
raise IncompleteRead(b'')
http.client.IncompleteRead: IncompleteRead(0 bytes read)
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/usr/lib/python3.13/site-packages/urllib3/response.py", line 779, in _error_catcher
yield
File "/usr/lib/python3.13/site-packages/urllib3/response.py", line 904, in _raw_read
data = self._fp_read(amt, read1=read1) if not fp_closed else b""
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.13/site-packages/urllib3/response.py", line 887, in _fp_read
return self._fp.read(amt) if amt is not None else self._fp.read()
~~~~~~~~~~~~~^^^^^
File "/usr/lib/python3.13/http/client.py", line 473, in read
return self._read_chunked(amt)
~~~~~~~~~~~~~~~~~~^^^^^
File "/usr/lib/python3.13/http/client.py", line 609, in _read_chunked
raise IncompleteRead(b''.join(value)) from exc
http.client.IncompleteRead: IncompleteRead(0 bytes read)
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<python-input-1>", line 1, in <module>
list(docker.APIClient().push("localhost:5000/hello-world:latest", stream=True, decode=True))
~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/path/to/docker-py/docker/api/client.py", line 360, in _stream_helper
yield from json_stream(self._stream_helper(response, False))
File "/path/to/docker-py/docker/utils/json_stream.py", line 60, in split_buffer
for data in stream_as_text(stream):
~~~~~~~~~~~~~~^^^^^^^^
File "/path/to/docker-py/docker/utils/json_stream.py", line 16, in stream_as_text
for data in stream:
^^^^^^
File "/path/to/docker-py/docker/api/client.py", line 365, in _stream_helper
data = reader.read(1)
File "/usr/lib/python3.13/site-packages/urllib3/response.py", line 980, in read
data = self._raw_read(amt)
File "/usr/lib/python3.13/site-packages/urllib3/response.py", line 903, in _raw_read
with self._error_catcher():
~~~~~~~~~~~~~~~~~~~^^
File "/usr/lib/python3.13/contextlib.py", line 162, in __exit__
self.gen.throw(value)
~~~~~~~~~~~~~~^^^^^^^
File "/usr/lib/python3.13/site-packages/urllib3/response.py", line 806, in _error_catcher
raise ProtocolError(f"Connection broken: {e!r}", e) from e
urllib3.exceptions.ProtocolError: ('Connection broken: IncompleteRead(0 bytes read)', IncompleteRead(0 bytes read))
A simple workaround is to pass auth_config={}:
list(docker.APIClient().push("localhost:5000/hello-world:latest", stream=True, decode=True, auth_config={}))
This bug can be fixed on Docker SDK for Python's side with the following patch:
diff --git a/docker/api/image.py b/docker/api/image.py
index 85109473..6e28894e 100644
--- a/docker/api/image.py
+++ b/docker/api/image.py
@@ -484,6 +484,8 @@ class ImageApiMixin:
header = auth.get_config_header(self, registry)
if header:
headers['X-Registry-Auth'] = header
+ else:
+ headers['X-Registry-Auth'] = auth.encode_header({})
else:
log.debug('Sending supplied auth config')
headers['X-Registry-Auth'] = auth.encode_header(auth_config)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in docker/api/image.py around ImageApiMixin's push authentication handling, then reproduce the failure with Docker 28.3.3 using the shell setup and APIClient().push() example. Compare behavior with and without auth_config={}; done means the no-auth push no longer ends in the reported EOF while the existing authenticated path remains functional.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- docker, python
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 35/100