{"record":{"id":"7f52c4f4ea6b12a5","repo":"vxcontrol/pentagi","slug":"truncated-exec-stream-w","errorCode":null,"errorMessage":"truncated exec stream: %w","messagePattern":"truncated exec stream: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/pkg/docker/client.go","lineNumber":1071,"sourceCode":"\treturn entries, false\n}\n\n// demuxExecStdout reads a non-TTY Docker exec stream — stdout and stderr\n// interleaved as frames with an 8-byte header (stream id + big-endian size) —\n// and returns only the stdout bytes, erroring if stdout exceeds maxStdout so a\n// compromised sandbox can't stream unbounded output into memory.\nfunc demuxExecStdout(r io.Reader, maxStdout int) ([]byte, error) {\n\tvar stdout bytes.Buffer\n\theader := make([]byte, 8)\n\tfor {\n\t\tif _, err := io.ReadFull(r, header); err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\tbreak // clean end at a frame boundary\n\t\t\t}\n\t\t\t// A header cut short (ErrUnexpectedEOF) means the stream was truncated\n\t\t\t// mid-frame — the listing is incomplete, so fail rather than silently\n\t\t\t// dropping the tail.\n\t\t\treturn nil, fmt.Errorf(\"truncated exec stream: %w\", err)\n\t\t}\n\t\tsize := int64(binary.BigEndian.Uint32(header[4:8]))\n\t\tif size == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tswitch header[0] {\n\t\tcase 1: // stdout\n\t\t\tif _, err := io.CopyN(&stdout, r, size); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tif stdout.Len() > maxStdout {\n\t\t\t\treturn nil, fmt.Errorf(\"listing output exceeded %d bytes\", maxStdout)\n\t\t\t}\n\t\tcase 3: // systemerr — a daemon-level error injected mid-stream; surface it\n\t\t\tvar msg bytes.Buffer\n\t\t\t_, _ = io.CopyN(&msg, r, size)\n\t\t\treturn nil, fmt.Errorf(\"docker exec systemerr: %s\", strings.TrimSpace(msg.String()))\n\t\tdefault: // stderr and anything else — discard","sourceCodeStart":1053,"sourceCodeEnd":1089,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/docker/client.go#L1053-L1089","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"var listing ContainerDirListing\nerr := retry.Do(func() error {\n    var e error\n    listing, e = client.ListContainerDir(ctx, containerID, dir)\n    return e\n}, retry.Attempts(3), retry.RetryIf(func(e error) bool {\n    return strings.Contains(e.Error(), \"truncated exec stream\")\n}))","preventionTips":["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"],"tags":["docker","stream","truncation","network"],"backgroundTag":"docker-exec-stream-truncated","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}