I was updating the Android version of one of my wallpaper apps and ran the build. It stopped with a "duplicate resources" error I had never seen before.
I had not touched the source. Yet the build output folder was full of files with doubled names. When I counted, there were more than 80 conflicted copies.
The cause was not code. The sync client treated files that are recreated on every build as edits from another device, and duplicated them.
Conflicted copies come from files that get rebuilt
When Dropbox decides the same file changed in two places, it keeps a copy named like (Name's conflicted copy 2026-10-21).
For a document a person edits, that is a kindness. Build output is different. A crash-reporting plugin regenerates a certain file on every build, and when that rewrite overlaps with a sync write-back, a second version appears in the same place and the copies pile up.
The build tool picks up everything in the output folder, copies included. Two resources with the same name, and it stops on "duplicate".
The table below is the split the two scripts that follow are built on.
| Location | Who writes it | If a conflicted copy appears |
|---|---|---|
| Source (src, etc.) | You | Do not delete; inspect and decide |
| build / .gradle | The build tool | Safe to delete (rebuilt next time) |
| Pods / xcuserdata | Package manager / Xcode | Safe to delete (regenerable) |
Count first: know what is where
Before cleaning anything, count. The point is to learn whether a conflicted copy has landed somewhere you must not delete from.
#!/usr/bin/env python3
"""Count Dropbox conflicted copies. Copies under generated folders are tallied separately."""
import os, re, sys, collections
CONFLICT = re.compile(
r"\((?:.+?)['’]s conflicted copy \d{4}-\d{2}-\d{2}(?: \d+)?\)|\(.*競合コピー.*\)"
)
GENERATED = {"build", ".gradle", "Pods", ".swiftpm", "xcuserdata", "node_modules", ".next"}
def scan(root):
total, scanned = 0, 0
by_dir = collections.Counter()
for dirpath, dirnames, filenames in os.walk(root):
dirnames[:] = [d for d in dirnames if d != ".git"] # never read .git
for name in filenames:
scanned += 1
if CONFLICT.search(name):
total += 1
parts = set(os.path.relpath(dirpath, root).split(os.sep))
hit = sorted(parts & GENERATED)
by_dir[hit[0] if hit else "(source)"] += 1
return scanned, total, by_dir
if __name__ == "__main__":
scanned, total, by_dir = scan(sys.argv[1])
print(f"scanned={scanned} conflicts={total}")
for k, v in by_dir.most_common():
print(f" {k}: {v}")
sys.exit(1 if total else 0)This answers not "are there any" but "how many, and in which kind of place" in one pass. The exit code lets a pre-build guard or a routine check use it later.
Why it is written this way: the pattern covers both English and Japanese names, because Dropbox localizes the label.
I ran it on a small folder shaped like a build output:
scanned=5 conflicts=4
build: 3
(source): 1I also ran it across my whole working folder (a little under 98,000 files). It found 0 conflicted copies and took about 17 seconds. On a large tree, plan on tens of seconds, and run it once in the morning or on the project folder only, not on every build.