PostHog / PostHog/posthog

Intercom "Update contacts" destination: 409 conflict race when concurrent invocations create the same new contact

Open
#72,192 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Python
Stars
39.9k
Forks
3.4k
Avg merge
6h 51m
Merged PRs (30d)
232

Description

Bug Description

What happens
The Intercom contacts destination (built from the official template) fails with http_4xx when two invocations for the same person run near-simultaneously and the contact doesn't exist in Intercom yet:

Error from intercom api (status 409): {'type': 'error.list', 'errors': [{'code': 'conflict', 'message': 'A contact matching those details already exists with id=...'}]}

Why it happens
The template does search-by-email → create-if-not-found. For a brand-new user, $identify and a $set-carrying event (e.g. sign_up, login, $screen) often arrive within the same few seconds. Both invocations search, both find nothing, both POST a create — the second one gets the 409 and the invocation is marked FAILED. Verified in our project (EU Cloud): within 10s of an $identify, the same users routinely fire multiple $set-carrying events, so any destination matching both Identify and Set person properties races on every new contact.

Expected behavior
The template should treat 409 as "contact exists": parse the id from the error body and retry as an update (PUT /contacts/{id}), i.e. behave as an upsert. Alternatively, use Intercom's upsert semantics where available.

Impact
Every new contact produces failed invocations that pollute error monitoring; data self-heals on the next event but the noise makes real failures hard to spot. Currently working around it by forking the source code, which detaches us from template updates.

Environment

  • PostHog EU Cloud
  • Destination: Intercom — Update contacts (official template, OAuth connection)
  • Reproducible: consistently, on any contact new to Intercom with two matched events within a few seconds
Debug info
- [ ] PostHog Cloud, Debug information: [please copy/paste from https://us.posthog.com/settings/project-details#variables or https://eu.posthog.com/settings/project-details#variables]
- [ ] PostHog Hobby self-hosted with `docker compose`, version/commit: [please provide]
- [ ] PostHog self-hosted with Kubernetes (deprecated, see [`Sunsetting Kubernetes support`](https://posthog.com/blog/sunsetting-helm-support-posthog)), version/commit: [please provide]

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at the official Intercom contacts destination template and trace the search-by-email, create-if-not-found path described in the report. Reproduce concurrent identify and $set-carrying events for a new contact; done means a create race treats the 409 response as an existing contact, updates it, and no longer marks the invocation failed, with a regression test for the race.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
api, backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.