vxcontrol/pentagi · error
truncated exec stream: %w
Error message
truncated exec stream: %w
What it means
demuxExecStdout parses the Docker multiplexed stream (8-byte frames: stream id + big-endian size). If reading an 8-byte frame header fails with anything other than clean io.EOF — typically io.ErrUnexpectedEOF — the stream was cut off mid-frame, meaning the listing tail was lost. The function deliberately errors instead of silently returning a partial listing.
Source
Thrown at backend/pkg/docker/client.go:1071
return entries, false
}
// demuxExecStdout reads a non-TTY Docker exec stream — stdout and stderr
// interleaved as frames with an 8-byte header (stream id + big-endian size) —
// and returns only the stdout bytes, erroring if stdout exceeds maxStdout so a
// compromised sandbox can't stream unbounded output into memory.
func demuxExecStdout(r io.Reader, maxStdout int) ([]byte, error) {
var stdout bytes.Buffer
header := make([]byte, 8)
for {
if _, err := io.ReadFull(r, header); err != nil {
if err == io.EOF {
break // clean end at a frame boundary
}
// A header cut short (ErrUnexpectedEOF) means the stream was truncated
// mid-frame — the listing is incomplete, so fail rather than silently
// dropping the tail.
return nil, fmt.Errorf("truncated exec stream: %w", err)
}
size := int64(binary.BigEndian.Uint32(header[4:8]))
if size == 0 {
continue
}
switch header[0] {
case 1: // stdout
if _, err := io.CopyN(&stdout, r, size); err != nil {
return nil, err
}
if stdout.Len() > maxStdout {
return nil, fmt.Errorf("listing output exceeded %d bytes", maxStdout)
}
case 3: // systemerr — a daemon-level error injected mid-stream; surface it
var msg bytes.Buffer
_, _ = io.CopyN(&msg, r, size)
return nil, fmt.Errorf("docker exec systemerr: %s", strings.TrimSpace(msg.String()))
default: // stderr and anything else — discardView on GitHub (pinned to ea665308ba)
Solutions
- Retry the listing — truncation is usually transient (connection reset)
- Check the container's exit/OOM state (`docker inspect`, dmesg for OOM kills)
- If using a remote daemon, remove or raise proxy/stream idle timeouts
- Reduce listing size (narrower dir) so the stream completes faster
Defensive patterns
Strategy: retry
Try / catch
var listing ContainerDirListing
err := retry.Do(func() error {
var e error
listing, e = client.ListContainerDir(ctx, containerID, dir)
return e
}, retry.Attempts(3), retry.RetryIf(func(e error) bool {
return strings.Contains(e.Error(), "truncated exec stream")
})) Prevention
- Use reliable transport to the daemon (local socket or stable network)
- Prevent container OOM kills (set memory limits appropriately)
- Reduce output volume by listing smaller directories
- Remove idle-stream timeouts on intermediate proxies
When it happens
Trigger: Docker daemon or network drops the exec stream mid-frame: container killed/OOM'd while find wrote output, TCP connection reset against a remote daemon, daemon restart, or proxy timeout between client and daemon.
Common situations: Remote Docker (DOCKER_HOST=tcp://...) behind a load balancer with idle/stream timeouts; container OOM-killed during a large listing; unstable VPN/network to a remote host.
Related errors
- failed to copy output: %w
- failed to ensure docker network %s: %w
- failed to pull default image '%s': %w
- failed to attach list exec for '%s': %w
- failed to read list output for '%s': %w
AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01).
Data as JSON: /api/errors/7f52c4f4ea6b12a5.
Report an issue: GitHub.