When using the Docker Engine API to execute commands inside a container, some users report seeing garbage characters or random symbols appearing in the API response. These characters do not appear when the same command is executed interactively in the terminal, and they often show up inconsistently. This behavior can be confusing, but it’s actually expected under certain conditions.
1. The Issue
A developer may use the Docker Exec API to run commands in containers, such as:
POST /containers/<container_id>/exec
POST /exec/<exec_id>/start
While the HTTP status code indicates success (200 OK), the response body sometimes includes random characters or extra symbols. This occurs intermittently and seems unpredictable. The problem appears only in API responses, not when running the same command with docker exec in a terminal.
2. Why It Happens: TTY and Frame Headers
The key lies in the Tty flag of the Docker Exec configuration.
When Tty is set to false, Docker returns output in a binary multiplexed stream with content type:
application/vnd.docker.raw-stream
In this format, Docker wraps the stdout and stderr streams into small binary frames. Each frame has an 8-byte header that describes the stream type and the payload size:
| Bytes | Description |
|---|---|
| 0 | Stream type (0 = stdin, 1 = stdout, 2 = stderr) |
| 1–3 | Padding (unused) |
| 4–7 | Payload size (big-endian uint32) |
| 8+ | Actual output bytes |
Example of a multiplexed output:
\x01\x00\x00\x00\x00\x00\x00\x1aHello from stdout!\n
\x02\x00\x00\x00\x00\x00\x00\x17Error message here\n
If a client (for example, a Python script) calls response.text and interprets this binary data as UTF-8 text, the header bytes are treated as printable characters. Depending on their byte values, they may appear as symbols such as 5, Q, or @. The randomness comes from variations in frame sizes, timing, and how the binary stream is chunked across requests.
3. Why It Works Fine in a Terminal
When you run docker exec from the CLI, the Docker client automatically demultiplexes the binary stream. It strips away the 8-byte headers and merges stdout and stderr into a clean text output before displaying it.
In contrast, when using the raw Docker API, your program receives the unprocessed binary frames directly. Unless you explicitly parse those frames, you’ll see the extra header bytes in the output.
4. Solutions
Option 1: Demultiplex the Binary Stream
Manually parse the application/vnd.docker.raw-stream response and reconstruct the text.
def demultiplex_docker_stream(raw_bytes: bytes) -> str:
"""Decode Docker's raw multiplexed stream."""
result = []
offset = 0
while offset < len(raw_bytes):
if offset + 8 > len(raw_bytes):
break
stream_type = raw_bytes[offset]
frame_size = int.from_bytes(raw_bytes[offset+4:offset+8], byteorder='big')
payload_start = offset + 8
payload_end = payload_start + frame_size
if payload_end > len(raw_bytes):
break
payload = raw_bytes[payload_start:payload_end]
result.append(payload.decode('utf-8', errors='replace'))
offset = payload_end
return ''.join(result)
# usage
ascii_text = demultiplex_docker_stream(response.content)
This method removes binary headers and outputs readable text.
Option 2: Use TTY Mode
Setting Tty: true changes how Docker returns data:
{
"AttachStdin": false,
"AttachStdout": true,
"AttachStderr": true,
"Tty": true,
"Cmd": ["sh", "-c", "your command"]
}
When Tty is enabled, Docker disables multiplexing and returns plain text. This merges stdout and stderr but avoids any binary header bytes.
Option 3: Use the Docker SDK
The official Python SDK handles demultiplexing internally:
import docker
client = docker.DockerClient(base_url='unix://var/run/docker.sock')
exec_id = client.api.exec_create(container_id, cmd)
output = client.api.exec_start(exec_id, demux=True) # demux=True splits stdout/stderr
This approach provides properly decoded data with no manual parsing required.
5. Summary
Seeing garbage characters in Docker Exec API responses is not a corruption or a network problem. It’s an expected side effect of reading a binary multiplexed stream as text. By either demultiplexing the response manually, enabling TTY mode, or using the official Docker SDK, you can ensure your command output remains clean and human-readable.
Enjoyed this article? Support my work with a coffee ☕ on Ko-fi.