{"record":{"id":"d8ec2449290357fb","repo":"dagger/dagger","slug":"removal-of-q-was-not-applied-to-the-merge-worktre","errorCode":null,"errorMessage":"removal of %q was not applied to the merge worktree","messagePattern":"removal of %q was not applied to the merge worktree","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/changeset.go","lineNumber":1958,"sourceCode":"\t\t\tcontinue\n\t\t}\n\t\tif wt == nil || !bytes.Equal(wt, headBytes) {\n\t\t\treturn fmt.Errorf(\"worktree content for %q differs from HEAD but git staged nothing (index stat cache failure)\", p)\n\t\t}\n\t}\n\n\tfor _, p := range paths.AllRemoved {\n\t\tif strings.HasSuffix(p, \"/\") {\n\t\t\t// A removed directory can coexist with paths the same changeset\n\t\t\t// re-adds beneath it; only file removals are checkable here.\n\t\t\tcontinue\n\t\t}\n\t\tfull, err := RootPathWithoutFinalSymlink(ws.root, path.Join(ws.dir, p))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif _, err := os.Lstat(full); !errors.Is(err, os.ErrNotExist) {\n\t\t\treturn fmt.Errorf(\"removal of %q was not applied to the merge worktree\", p)\n\t\t}\n\t}\n\n\tif len(files) == 0 {\n\t\treturn nil\n\t}\n\tif content.diff.Self() == nil {\n\t\treturn fmt.Errorf(\"changeset declared file changes %v but materialized no diff content\", files)\n\t}\n\treturn ws.withDiffDir(ctx, content, func(diffDir string) error {\n\t\tfor _, p := range files {\n\t\t\tsame, err := ws.worktreeMatchesDiff(p, diffDir)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif !same {\n\t\t\t\treturn fmt.Errorf(\"applied content for %q did not land in the merge worktree\", p)\n\t\t\t}","sourceCodeStart":1940,"sourceCodeEnd":1976,"githubUrl":"https://github.com/dagger/dagger/blob/82ba2681dbe30d3547a1dc50ea495900ab5b6047/core/changeset.go#L1940-L1976","documentation":"verifyBranchContentLanded checks that every path the changeset declares as removed is actually gone from the merge worktree (after resolving the root path without final symlinks). If os.Lstat finds the path still exists (anything other than ErrNotExist), Dagger fails rather than silently keeping a file that was supposed to be deleted. The empty staged diff triggered this deeper verification.","triggerScenarios":"A changeset removing path p (p in paths.AllRemoved) applied to the branch, `git add -A`/`diff --cached` staged nothing, and Lstat on root/dir/p still finds the entry - meaning the removal never landed or the path was recreated.","commonSituations":"The base tree had a file at p that the removal failed to delete due to apply-content races; another changeset in the same merge recreates p; path casing or symlink resolution differences causing Lstat to hit a different entry than the one removed.","solutions":["Check whether another changeset in the same merge re-adds the same path; resolve the conflicting removal/re-add between changesets.","Retry - if it was an apply race, the rebuilt workspace will not reproduce it.","Verify the removal path matches the base tree exactly (no trailing-slash/casing/symlink mismatch) in the code that constructs the changeset (Directory.withoutDirectory etc.).","If reproducible with a single unambiguous removal, report as a merge-integrity bug with the path and changeset construction code."],"exampleFix":"// before: two changesets where d1 removes \"out/\" and d2 writes \"out/result.txt\"\n// after: sequence them or remove only specific files:\n//   d1 := base.withoutDirectory(\"out/tmp\")\n//   d2 := base.withNewFile(\"out/result.txt\", \"data\")","handlingStrategy":"validation","validationCode":"// Before merging, ensure no path is removed by one changeset and written by another:\nfunc removalWriteConflict(cs []Changeset) bool {\n    removed, written := map[string]bool{}, map[string]bool{}\n    for _, c := range cs {\n        for _, p := range c.Removed { removed[strings.TrimSuffix(p, \"/\")] = true }\n        for _, p := range append(c.Added, c.Modified...) { written[strings.TrimSuffix(p, \"/\")] = true }\n    }\n    for p := range removed { if written[p] { return true } }\n    return false\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Do not combine a removal of a path with a re-add of the same path in the same octopus merge.","Match removal paths exactly to base-tree paths (watch casing, trailing slashes, symlinks).","Remove individual files rather than whole directories when other changes touch that directory."],"tags":["git","merge","removal","integrity"],"backgroundTag":"merge-removal-not-applied","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"}