{"record":{"id":"845e4320404b6a6b","repo":"vxcontrol/pentagi","slug":"listing-container-directory-s-w","errorCode":null,"errorMessage":"listing container directory '%s': %w","messagePattern":"listing container directory '(.+?)': %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"backend/pkg/docker/client.go","lineNumber":1025,"sourceCode":"\tif err != nil {\n\t\treturn ContainerDirListing{}, fmt.Errorf(\"failed to inspect list exec for '%s': %w\", dirPath, err)\n\t}\n\tif inspect.ExitCode != 0 {\n\t\treturn ContainerDirListing{}, fmt.Errorf(\"list command failed for '%s' with exit code %d: %s\", dirPath, inspect.ExitCode, string(output))\n\t}\n\n\tentryPaths, truncated := parseFindEntries(output)\n\n\tstats, failures := statContainerEntries(ctx, entryPaths, containerListWorkers, func(ctx context.Context, entryPath string) (container.PathStat, error) {\n\t\treturn dc.ContainerStatPath(ctx, containerID, entryPath)\n\t})\n\n\t// A cancelled context is a directory-level fault (the client is gone), not a\n\t// partial listing, so surface it as an error. Entries that individually failed\n\t// to stat are carried in Failures — the find exec already proved the container\n\t// alive, so a live directory degrades rather than 500s even if every entry failed.\n\tif err := ctx.Err(); err != nil {\n\t\treturn ContainerDirListing{}, fmt.Errorf(\"listing container directory '%s': %w\", dirPath, err)\n\t}\n\n\tlisting := ContainerDirListing{Files: stats, Truncated: truncated}\n\tfor _, f := range failures {\n\t\tlisting.Failures = append(listing.Failures, ContainerEntryError{\n\t\t\tName: path.Base(f.name),\n\t\t\tPath: f.name,\n\t\t\tErr:  f.err,\n\t\t})\n\t}\n\n\treturn listing, nil\n}\n\n// parseFindEntries splits `find -print0` output (NUL-delimited absolute paths),\n// dropping empties, and caps the result at maxListEntries — returning truncated=true\n// so the caller reports the partiality instead of failing or silently dropping.\nfunc parseFindEntries(output []byte) (entries []string, truncated bool) {","sourceCodeStart":1007,"sourceCodeEnd":1043,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/docker/client.go#L1007-L1043","documentation":"After stat'ing every entry, ListContainerDir checks ctx.Err() and fails the whole listing if the context was cancelled, treating cancellation as a directory-level fault (the client is gone) rather than returning a partial listing. The wrapped cause is context.Canceled or context.DeadlineExceeded.","triggerScenarios":"The caller's context is cancelled or its deadline expires while ListContainerDir is running (per-entry ContainerStatPath calls against a large directory), or the HTTP request that triggered the listing is aborted by the client.","commonSituations":"Listing huge directories against a slow/remote daemon exceeding deadlines; aborted HTTP requests; service shutdown draining in-flight work.","solutions":["Increase the context deadline passed to ListContainerDir for large directories","List a narrower directory to reduce per-entry stat work","Ensure the caller does not cancel the context before results are consumed","Retry with a fresh, longer-lived context if the cancellation was transient"],"exampleFix":"// before: short timeout expires mid-listing\nctx, cancel := context.WithTimeout(ctx, 2*time.Second)\n// after: size the deadline to the directory\nctx, cancel := context.WithTimeout(ctx, 30*time.Second)\nlisting, err := dc.ListContainerDir(ctx, id, dir)","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)\ndefer cancel()\nlisting, err := client.ListContainerDir(ctx, containerID, dir)\nif err != nil {\n    if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) {\n        return retryListingWithLargerDeadline(containerID, dir)\n    }\n    return err\n}","preventionTips":["Size context deadlines to directory size and daemon latency","Don't cancel parent contexts while listings are in flight","For huge dirs, list incrementally by subdirectory","Propagate request cancellation only after results are no longer needed"],"tags":["context","timeout","cancellation"],"backgroundTag":"context-cancelled","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}