{"record":{"id":"1bcd34f4bfe8d96f","repo":"vxcontrol/pentagi","slug":"failed-to-read-file-s-content-w","errorCode":null,"errorMessage":"failed to read file '%s' content: %w","messagePattern":"failed to read file '(.+?)' content: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/pkg/tools/terminal.go","lineNumber":415,"sourceCode":"\t\t\tbuffer.WriteString(\n\t\t\t\tfmt.Sprintf(\"'%s' file content (with size %d bytes) shown below:\\n\",\n\t\t\t\t\ttarHeader.Name, tarHeader.Size,\n\t\t\t\t),\n\t\t\t)\n\t\t}\n\n\t\tconst maxReadFileSize int64 = 100 * 1024 * 1024 // 100 MB limit\n\t\tif tarHeader.Size > maxReadFileSize {\n\t\t\treturn \"\", fmt.Errorf(\"file '%s' size %d exceeds maximum allowed size %d\", tarHeader.Name, tarHeader.Size, maxReadFileSize)\n\t\t}\n\t\tif tarHeader.Size < 0 {\n\t\t\treturn \"\", fmt.Errorf(\"file '%s' has invalid size %d\", tarHeader.Name, tarHeader.Size)\n\t\t}\n\n\t\tvar fileContent = make([]byte, tarHeader.Size)\n\t\t_, err = tarReader.Read(fileContent)\n\t\tif err != nil && err != io.EOF {\n\t\t\treturn \"\", fmt.Errorf(\"failed to read file '%s' content: %w\", tarHeader.Name, err)\n\t\t}\n\t\tbuffer.Write(fileContent)\n\n\t\tif stats.Mode.IsDir() {\n\t\t\tbuffer.WriteString(\"\\n\\n\")\n\t\t}\n\t}\n\n\treturn buffer.String(), nil\n}\n\nfunc (t *terminal) WriteFile(ctx context.Context, flowID int64, content string, path string) (string, error) {\n\tif path == \"\" {\n\t\treturn \"\", fmt.Errorf(\"path is required and cannot be empty\")\n\t}\n\n\tif err := t.writeFileToContainer(ctx, flowID, path, content); err != nil {\n\t\treturn \"\", err","sourceCodeStart":397,"sourceCodeEnd":433,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/tools/terminal.go#L397-L433","documentation":"After allocating fileContent of tarHeader.Size bytes, the code reads the tar entry body; any read error other than io.EOF (which is normal for exact-size reads) is wrapped with this message. io.EOF is intentionally accepted because tar.Reader returns EOF exactly when the full entry was consumed, so this error means the stream died mid-entry.","triggerScenarios":"The tar stream from CopyFromContainer terminates before tarHeader.Size bytes are delivered — connection reset to a remote daemon, daemon restart, reader closed by timeout, or disk/IO error on the Docker host while assembling the archive.","commonSituations":"Reading a large file from a container on a remote/unstable Docker host; host under memory pressure killing the daemon mid-copy; container removed or restarted concurrently with the copy; misconfigured proxy between client and daemon cutting long-lived streams.","solutions":["Retry the entire read (CopyFromContainer + tar loop) — mid-stream truncation is usually transient","Use errors.Is(err, io.ErrUnexpectedEOF) to distinguish truncation from other IO errors and log accordingly","Check Docker daemon logs and host disk health (`dmesg`, `docker info`) when it recurs","For large files, stream with io.CopyFull / io.ReadFull into the buffer with timeouts instead of a single Read"],"exampleFix":"// before\n_, err = tarReader.Read(fileContent)\nif err != nil && err != io.EOF {\n    return \"\", fmt.Errorf(\"failed to read file '%s' content: %w\", tarHeader.Name, err)\n}\n// after\nif _, err := io.ReadFull(tarReader, fileContent); err != nil && err != io.EOF && !errors.Is(err, io.ErrUnexpectedEOF) {\n    return \"\", fmt.Errorf(\"failed to read file '%s' content: %w\", tarHeader.Name, err)\n}","handlingStrategy":"try-catch","validationCode":"// Pre-call: confirm daemon connectivity to reduce mid-stream failures\nif _, err := dockerClient.Ping(ctx); err != nil {\n    return fmt.Errorf(\"docker daemon unstable: %w\", err)\n}","typeGuard":"func isMidStreamIO(err error) bool {\n    return err != nil && !errors.Is(err, io.EOF) && (errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, syscall.ECONNRESET) || errors.Is(err, syscall.EPIPE))\n}","tryCatchPattern":"content, err := tool.ReadFile(ctx, flowID, path)\nif err != nil && strings.Contains(err.Error(), \"failed to read file\") {\n    if isRetryable(err) {\n        content, err = tool.ReadFile(ctx, flowID, path) // one retry\n    }\n}","preventionTips":["Use io.ReadFull instead of a single Read for exact-size tar entries","Retry mid-stream failures; they are usually transient","Monitor Docker host disk/memory health","Avoid removing or restarting the container during an in-flight copy"],"tags":["docker","tar","io"],"backgroundTag":"corrupt-tar-archive","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}