{"record":{"id":"46f79b208beb5090","repo":"dagger/dagger","slug":"collect-delta-w","errorCode":null,"errorMessage":"collect delta: %w","messagePattern":"collect delta: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/changeset_delta.go","lineNumber":43,"sourceCode":"\t// modifiedCandidates are files whose metadata differs; content may still\n\t// be identical (e.g. an mtime-only change), so they must be verified\n\t// before being reported as modified.\n\tmodifiedCandidates []string\n\tremovedFiles       []string\n\taddedDirs          []string\n\tremovedDirs        []string\n}\n\n// computeChangesetPathsDelta computes ChangesetPaths by walking filesystem\n// metadata and reading content only for files the metadata can't rule out,\n// instead of content-diffing both full trees like computeChangesetPaths.\n// Rename detection and line counts still come from git, but scoped to the\n// changed files only. When withStats is true it also returns per-path\n// line-change counts matching `git diff --numstat` semantics.\nfunc computeChangesetPathsDelta(ctx context.Context, beforeDir, afterDir string, withStats bool) (*ChangesetPaths, map[string]lineChanges, error) {\n\tdelta, err := collectChangesetDelta(ctx, beforeDir, afterDir)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"collect delta: %w\", err)\n\t}\n\n\tmodified, err := verifyModifiedFiles(ctx, beforeDir, afterDir, delta.modifiedCandidates)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"verify modified files: %w\", err)\n\t}\n\n\tfc := fileChanges{\n\t\tAdded:    delta.addedFiles,\n\t\tModified: modified,\n\t\tRemoved:  delta.removedFiles,\n\t}\n\n\t// Renames are always an added/removed pair, so the changed files are the\n\t// complete candidate set; running git over just them yields the same\n\t// pairings as a full-tree diff.\n\tdetectRenames := len(delta.addedFiles) > 0 && len(delta.removedFiles) > 0\n\tmaterializeModified := withStats && len(modified) > 0","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/dagger/dagger/blob/82ba2681dbe30d3547a1dc50ea495900ab5b6047/core/changeset_delta.go#L25-L61","documentation":"computeChangesetPathsDelta wraps any failure of collectChangesetDelta as \"collect delta: %w\". collectChangesetDelta double-walks both directory trees comparing stat metadata to find added/removed/modified candidates. A failure here means the filesystem walk itself failed (unreadable directory, context cancellation, lstat errors), so no changeset delta can be computed.","triggerScenarios":"Calling computeChangesetPathsDelta (directly or via requireSamePaths/requireSameNumStat) with a beforeDir or afterDir that cannot be walked: path does not exist, permission denied, or context cancelled mid-walk.","commonSituations":"The before/after scratch directory was cleaned up before the call; a subdirectory changed permissions during traversal; context deadline exceeded on very large trees; path casing/mount mismatch between the two sides.","solutions":["Check the wrapped cause: ENOENT/permission errors point at beforeDir/afterDir paths","Verify both directories exist and are readable before calling the API","Extend the context timeout if walking a very large tree","Ensure nothing deletes or mutates the source dirs concurrently with the delta computation"],"exampleFix":"// before\ndelta, err := computeChangesetPathsDelta(ctx, beforeDir, afterDir, true)\n// after\nif _, err := os.Stat(beforeDir); err != nil { return fmt.Errorf(\"beforeDir missing: %w\", err) }\nif _, err := os.Stat(afterDir); err != nil { return fmt.Errorf(\"afterDir missing: %w\", err) }\ndelta, err := computeChangesetPathsDelta(ctx, beforeDir, afterDir, true)","handlingStrategy":"validation","validationCode":"for _, d := range []string{beforeDir, afterDir} {\n  fi, err := os.Stat(d)\n  if err != nil { return fmt.Errorf(\"delta dir %s: %w\", d, err) }\n  if !fi.IsDir() { return fmt.Errorf(\"%s is not a directory\", d) }\n}","typeGuard":null,"tryCatchPattern":"if err != nil {\n  if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { /* retry with longer budget */ }\n  return fmt.Errorf(\"delta collection failed: %w\", err)\n}","preventionTips":["Stat both directories before computing the delta","Keep before/after trees immutable during comparison","Size the context timeout to tree scale"],"tags":["filesystem","diff","walk"],"backgroundTag":"directory-walk-failed","analyzedSha":"82ba2681dbe30d3547a1dc50ea495900ab5b6047","analyzedAt":"2026-09-05T07:21:37.930Z","contentChangedAt":"2026-09-05T07:21:37.930Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}