dagger/dagger · error

collect delta: %w

Error message

collect delta: %w

What it means

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.

Source

Thrown at core/changeset_delta.go:43

	// modifiedCandidates are files whose metadata differs; content may still
	// be identical (e.g. an mtime-only change), so they must be verified
	// before being reported as modified.
	modifiedCandidates []string
	removedFiles       []string
	addedDirs          []string
	removedDirs        []string
}

// computeChangesetPathsDelta computes ChangesetPaths by walking filesystem
// metadata and reading content only for files the metadata can't rule out,
// instead of content-diffing both full trees like computeChangesetPaths.
// Rename detection and line counts still come from git, but scoped to the
// changed files only. When withStats is true it also returns per-path
// line-change counts matching `git diff --numstat` semantics.
func computeChangesetPathsDelta(ctx context.Context, beforeDir, afterDir string, withStats bool) (*ChangesetPaths, map[string]lineChanges, error) {
	delta, err := collectChangesetDelta(ctx, beforeDir, afterDir)
	if err != nil {
		return nil, nil, fmt.Errorf("collect delta: %w", err)
	}

	modified, err := verifyModifiedFiles(ctx, beforeDir, afterDir, delta.modifiedCandidates)
	if err != nil {
		return nil, nil, fmt.Errorf("verify modified files: %w", err)
	}

	fc := fileChanges{
		Added:    delta.addedFiles,
		Modified: modified,
		Removed:  delta.removedFiles,
	}

	// Renames are always an added/removed pair, so the changed files are the
	// complete candidate set; running git over just them yields the same
	// pairings as a full-tree diff.
	detectRenames := len(delta.addedFiles) > 0 && len(delta.removedFiles) > 0
	materializeModified := withStats && len(modified) > 0

View on GitHub (pinned to 82ba2681db)

Solutions

  1. Check the wrapped cause: ENOENT/permission errors point at beforeDir/afterDir paths
  2. Verify both directories exist and are readable before calling the API
  3. Extend the context timeout if walking a very large tree
  4. Ensure nothing deletes or mutates the source dirs concurrently with the delta computation

Example fix

// before
delta, err := computeChangesetPathsDelta(ctx, beforeDir, afterDir, true)
// after
if _, err := os.Stat(beforeDir); err != nil { return fmt.Errorf("beforeDir missing: %w", err) }
if _, err := os.Stat(afterDir); err != nil { return fmt.Errorf("afterDir missing: %w", err) }
delta, err := computeChangesetPathsDelta(ctx, beforeDir, afterDir, true)
Defensive patterns

Strategy: validation

Validate before calling

for _, d := range []string{beforeDir, afterDir} {
  fi, err := os.Stat(d)
  if err != nil { return fmt.Errorf("delta dir %s: %w", d, err) }
  if !fi.IsDir() { return fmt.Errorf("%s is not a directory", d) }
}

Try / catch

if err != nil {
  if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { /* retry with longer budget */ }
  return fmt.Errorf("delta collection failed: %w", err)
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of dagger/dagger@82ba2681db (2026-09-05). Data as JSON: /api/errors/46f79b208beb5090. Report an issue: GitHub.