# Fix SCIM PATCH failing on a read-only attribute

## TL;DR
The provider rejected the replace operation because the attribute is read-only in its schema - id, meta attributes, userName in some providers, or custom fields the provider locks down. Drop that attribute and PATCH only writable attributes. Check the provider's /Schemas endpoint to see exactly which attributes carry readOnly=true not guessing.

## The error
```text
PATCH the SCIM v2 user resource (DELETE) -> 400 Bad Request
{"schemas":["urn:ietf:params:scim:api:messages:2.0:Error"],"detail":"Attribute is read-only and cannot be modified","status":"400"}
```

## Use this skill
Agent? Search Vectle for this error:
```bash
curl -s "https://vectle.com/api/v1/search?q=scim%20replace%20operation%20failed%20read-only%20attribute"
```

## Fix it

### Step 1: Read the provider's schema to find the read-only attributes

```bash
curl -s "https://yourapp.example.com/scim/v2/Schemas/urn:ietf:params:scim:schemas:core:2.0:User" -H "your auth header bearer value]" | python3 -c "import json,sys; s=json.load(sys.stdin); print([a['name'] for a in s['attributes'] if a.get('readOnly')])"
```

Expected: a list naming the read-only attributes. The attribute in the failed PATCH should be on it.

### Step 2: Strip read-only attributes from the PATCH

```bash
Rewrite the PATCH operations to touch only mutable attributes. Drop id, meta sub-attributes, and anything flagged readOnly from the payload.
```

Expected: the payload contains only writable paths.

### Step 3: Re-send the PATCH

```bash
curl -s -X PATCH "https://yourapp.example.comthe SCIM v2 user resource (DELETE)" -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":"name.givenName","value":"[first name]"}]}'
```

Expected: HTTP 200 and the writable attribute updates.

### Step 4: Fix the IdP attribute mapping so it stops sending the read-only field

```bash
In the IdP provisioning attribute mappings, unmap or exclude the read-only target attribute so future syncs never include it in PATCH operations.
```

Expected: the preview no longer emits the read-only attribute.

### Step 5: Re-run the failed provisioning task

```bash
Retry the failed task in the IdP provisioning queue and confirm it completes.
```

Expected: success with no 400. The mapping fix prevents recurrence.

## When this applies

- SCIM PATCH/replace returns 400 naming a read-only attribute
- Provisioning updates fail while creates succeed
- The payload includes id, meta, or provider-locked fields

## When it doesn't

- The failure is 401 or 403 (credentials)
- The attribute should be writable but is rejected anyway (provider bug or wrong path syntax)
- Creates fail with 400 (schema violation on create, different issue)

## Compatibility

SCIM 2.0 per RFC 7644 PATCH semantics. Applies to any SCIM provider enforcing attribute mutability.

## Variant phrasings

### scim patch 400 read only attribute

Same fix. The schema endpoint is authoritative; provider docs can lag behind the actual enforcement.

### okta scim replace failed mutability

In Okta, the fix lives in the app's Profile Editor mappings - exclude the target attribute there.

## Why it happens

SCIM marks some attributes readOnly: returnable but never writable. Providers enforce this server-side, so a replace op naming one is rejected however correct the rest is. IdP mappings that push every profile field eventually collide with one.

## Edge cases

- userName is writable at create but read-only afterwards in several providers; the same mapping that creates fine then fails on update
- Some providers return 422 instead of 400 for the same violation; treat them identically
- Enterprise extension attributes have their own mutability flags; check the extension schema too

## If it still fails

- Capture the exact timestamp, the username, and the full error from the system log before changing anything else.
- Reproduce with a single test user so you are not debugging a crowd.
- If it worked before, diff the config against the last known good, then open a vendor ticket with the timestamp and request id. Never send secrets or private keys.

## Prevention

- Track credential and certificate expiry with alerts, not memory.
- Run a synthetic check per app daily so breakage pages you, not a user.
- Document mappings, URLs, and runbook steps where the next admin will find them.

## Provenance

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