Why Does GitLab Service Desk Create a New Issue for Every Reply?
It happens because GitLab doesn't recognize the reply as belonging to the original issue anymore, so its mailroom treats the email as a fresh support request and files it as one. Service Desk matches incoming mail to an existing issue by looking for a specific key GitLab embedded in the notification it sent out, in the To, Delivered-To, Cc, or a few other headers, per GitLab's incoming email documentation. If that key isn't present anywhere GitLab checks, there's no issue to attach the reply to, and the only thing left to do with an email arriving at a Service Desk address is open a new ticket.
The most common way to break the key is moving the issue to a different project after the customer already has a notification thread open. GitLab issue IDs are scoped to a single project, so a cross-project move closes the original issue and creates a new one at the destination. That's confirmed on GitLab's own forum, where several people reported the same trigger. The customer's inbox still points at the old issue. Their next reply, and any staff reply on the new one, has nothing in it that maps back correctly, and the loop breaks in both directions.
How the matching is supposed to work
Every Service Desk issue gets a unique reply-to address baked into the notification emails GitLab sends. It looks like an ordinary support address with an extra token folded in, a pattern GitLab calls sub-addressing. When a customer hits reply in their email client, most clients preserve that address somewhere in the headers even if it's not visible in the compose window, and GitLab's mailroom checks a fixed list of places for it: To first, then Delivered-To, X-Delivered-To, Envelope-To or X-Envelope-To, Received, X-Original-To, X-Forwarded-To, and Cc, in that order. Find the key in any of those, and the reply lands as a new comment on the right issue. GitLab also reads the References header, but only to relate the reply to the right discussion thread within an issue, not to decide which issue it belongs to.
None of this depends on the subject line, and none of it depends on the human reading the email. It's a header lookup, which is exactly why it's fragile in ways that aren't visible to anyone reading the thread normally.
What actually breaks it
Beyond the project-move case above, the same failure shows up for a few other reasons, and they all have the same shape. The key goes missing from every header GitLab checks.
- Corporate mail gateways rewrite headers. Some security or archiving gateways strip or rewrite
Delivered-Toand similar headers on the way through, which is invisible to the person hitting reply. - The customer forwards instead of replying, or pastes the question into a brand-new email to the general support alias. A forward carries none of the original headers that matter.
- CCing the intake address on an unrelated thread. If a customer or a teammate CCs the project's main Service Desk address on any email, GitLab has no per-issue key to find and opens a new ticket, because the intake address itself isn't tied to one issue.
A support inbox learns this the hard way
A customer's dashboard exports had started timing out on anything over a 30-day range, and Ferro Metrics takes bug reports through a GitLab Service Desk address feeding the platform-support project. Priyanka Bhatt, who runs support there, triaged the report, decided it was actually a backend problem, and moved the issue into ingest-pipeline so the right engineers had it on their board.
Three days later the customer replied to say the timeout was still happening:
Customer: Following up on this, still broken as of this morning. Exports over 30 days just spin.
The reply carried the key from the original notification, but that key pointed at an issue that no longer lived in platform-support in any form GitLab's mailroom would recognize as current. Unable to find a match, the mailroom treated the follow-up as a new report and opened a fresh issue in platform-support from the same email a minute later. Priyanka now had three artifacts for one bug: the original issue, closed and untouched; the moved copy in ingest-pipeline the engineer was actually working, with nothing in it to show the customer had followed up; and the new duplicate, sitting back in platform-support where nobody assigned to the real fix was watching. The customer had no idea any of this had happened. From their side, they'd just replied to an email.
How to keep it from happening
None of these fully eliminate the risk, but each closes off a real cause:
- Avoid moving Service Desk issues across projects once a customer thread is live. If the work genuinely belongs to another team, keep the customer-facing issue where it is and link a second internal issue for the engineering side, rather than relocating the one the customer's inbox is pointed at.
- If a move is unavoidable, close the loop by hand. Reply to the customer from the new issue's own address before they reply again, so their next email carries the new key instead of the dead one.
- Watch for duplicates from the same requester. GitLab's external participants show up by email address, so a second issue from the same address within a day or two of an existing one is worth a manual check before anyone starts working it fresh.
- Tell customers to reply, not forward or CC. It sounds trivial, but on a small support volume this single instruction removes most of the accidental duplicates.
Where this stops scaling
All four of those are habits, and habits decay under volume and headcount turnover. A team fielding a couple of Service Desk tickets a week can enforce "don't move live issues" by memory. A team fielding fifty a week, across a rotating support roster, will move issues without checking who's mid-thread on them, and duplicates will pile up quietly because nobody is scanning for a second issue from the same email address every morning.
That's the point where a layer outside GitLab's own mailroom logic earns its keep. Modem reads the same Service Desk activity through the GitLab integration, but it identifies the customer by who they are, not by which header survived the trip. A reply, a forward, a duplicate ticket, and a mention of the same bug in Slack all collapse into one tracked topic with the full history attached, regardless of which GitLab project the underlying issue ends up living in. We sell Modem, so factor that into how you read this recommendation, but the project-move breakage described above is something GitLab's own forum users flagged well before this guide existed.
For the parts of this that are about process rather than tooling, see how to run a feedback triage process and how to close the feedback loop with customers. Both apply whether or not the ticket started life as a Service Desk email.
The smallest fix you can make today
Add one line to your team's Service Desk runbook. Before moving any issue with a customer thread on it, reply from the current issue first. That single habit removes the most common cause of the split, and it costs nothing beyond remembering to do it.
