# Fix SCIM user creation failing with a 409 conflict

## TL;DR
A 409 on user creation means the user already exists at the service provider, usually with a different externalId than your IdP expects. Stop retrying the POST; fetch the existing user by userName and PATCH it active instead. Fix the externalId mapping so the next sync updates instead of creating.

## The error
```text
POST /scim/v2/Users -> 409 Conflict
{"schemas":["urn:ietf:params:scim:api:messages:2.0:Error"],"detail":"User already exists","status":"409"}
```

## Use this skill
Agent? Search Vectle for this error:
```bash
curl -s "https://vectle.com/api/v1/search?q=scim user creation 409 conflict failed"
```

## Fix it

### Step 1: Confirm the user exists at the provider

```bash
curl -s "https://yourapp.example.com/scim/v2/Users?filter=userName+eq+%22jdoe%40example.com%22" \
  -H "your auth header bearer value]" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('totalResults'), [r['id'] for r in d.get('Resources',[])])"
```

Expected: totalResults is 1 and you get back an existing user id. If it is 0, the 409 came from a different unique field.

### Step 2: PATCH the existing user instead of creating

```bash
curl -s -X PATCH "https://yourapp.example.com/scim/v2[HOME]/... id]" \
  -H "your auth header bearer value]" -H "Content-Type: application/scim+json" \
  -d '{"schemas":["urn:ietf:params:scim:api:messages:2.0:PatchOp"],"Operations":[{"op":"replace","path":"active","value":true}]}' | head -c 300
```

Expected: HTTP 200 and the user flips to active. The user is now provisioned without a duplicate.

### Step 3: Align the externalId mapping in your IdP

```bash
In your IdP provisioning settings, map externalId to the IdP user id (Okta: user.id, Entra: objectId) and re-push one test user with a provisioning log open.
```

Expected: The log shows an update (PATCH) instead of a create (POST) for the test user.

### Step 4: Re-run the failed provisioning job

```bash
In the IdP admin console, open provisioning, find the failed user, and choose retry or re-apply the app assignment.
```

Expected: The task completes with status success and the user appears active in the app.

### Step 5: Add check-before-create in your own SCIM client

```bash
Before every POST, GET /Users?filter=userName eq [login] and reuse the returned id when totalResults is 1.
```

Expected: Re-running the client twice produces zero 409s and zero duplicates.

## When this applies

- SCIM user creation returns 409 and the user never becomes active
- You suspect duplicate users are being created by the IdP sync
- You are building or debugging a SCIM 2.0 client or connector

## When it doesn't

- The failure is a 401 or 403 (fix credentials first)
- The failure is a 400 schema error (fix the payload shape)
- Users are missing entirely rather than conflicting

## Compatibility

SCIM 2.0 per RFC 7643 and 7644. Applies to Okta, Microsoft Entra ID, Google Workspace, and OneLogin SCIM connectors.

## Variant phrasings

### scim 409 user already exists on provision

Same root cause. The service provider keys uniqueness on userName, so a mismatched externalId reads as a duplicate.

### okta scim 409 conflict creating user

In Okta, open the provisioning task log for the user. The failed task usually shows a create attempt for a user the app already has.

### azure ad scim 409 on user sync

Entra provisions with objectId as the anchor. If the app matched on a different attribute before, the anchor pair splits and every sync retries a create.

## Why it happens

A 409 means uniqueness collided at the provider. The IdP believes this is a new user and sends POST /Users; the provider already holds that userName under a different externalId, so it rejects the create. Retrying the same POST can never succeed. The fix is always on the identity mapping: agree on one stable anchor attribute and update instead of create.

## Edge cases

- Soft-deleted users still collide: restore or purge them before re-creating
- userName is case-sensitive in some providers; jdoe and JDOE can fight forever
- Bulk imports that pre-seeded users before SCIM was enabled cause mass 409s on first sync

## If it still fails

- Capture the exact timestamp, the failing username, and the full error from the IdP system log before changing anything else.
- Reproduce with a single test user so you are not debugging a crowd.
- Check the IdP and app status pages; SSO and provisioning outages look exactly like config errors.
- If it worked before, diff the config against the last known good: certificates, URLs, attribute mappings, and credential expiry.
- Open a vendor ticket with the timestamp, the request id if there is one, and redacted config. Never send secrets or private keys.

## Prevention

- Track certificate and credential expiry with alerts, not memory.
- Run a synthetic login per SSO app daily so breakage pages you, not a user.
- Document attribute mappings where the next admin will actually find them.
- Test provisioning with a single user before bulk changes.
- Review app assignments quarterly; stale assignments cause half of provisioning errors.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_dwgJ66v0vT3xhwuFlpmZiw
