You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
warning(CLAUDE): Vapi PATCH on nested objects is REPLACE not deep-merge
On 2026-05-13 a Claude session wiped system prompts on three live-production
iForm barge assistants by PATCHing the model object without including
model.messages. The API replaced the entire model object — prompts gone,
calls ran with empty system prompt until the operator forced a restore.
Adds a prominent Critical Safety Rules section to the top of CLAUDE.md with:
- The exact failure mode and incident reference
- The mandatory GET-modify-PATCH-verify workflow with shell example
- A list of all affected nested objects (model, voice, transcriber, etc.)
- The 'patched X came back correct doesn't mean other fields survived' check
- A recommendation to prefer the gitops engine over direct API PATCH
Copy file name to clipboardExpand all lines: CLAUDE.md
+35Lines changed: 35 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,6 +7,41 @@ This repository uses two instruction sources for Claude:
7
7
8
8
When both files exist, follow both. If guidance overlaps, treat `AGENTS.md` as the canonical project playbook and use this file to reinforce Claude-specific behavior.
9
9
10
+
---
11
+
12
+
## ⚠️ CRITICAL SAFETY RULES — read before any direct Vapi API call
13
+
14
+
### Vapi PATCH on nested objects is REPLACE, not deep-merge
15
+
16
+
**The Vapi PATCH API does NOT deep-merge nested objects. When you PATCH a nested object (`model`, `voice`, `transcriber`, `messagePlan`, `analysisPlan`, `artifactPlan`, `voicemailDetection`, `startSpeakingPlan`, `stopSpeakingPlan`) with a partial body, the API REPLACES the entire object — wiping every field you didn't include.**
17
+
18
+
This wiped three live-production assistants' system prompts on 2026-05-13 (gitops-mudflap iForm barge fleet). The PATCH was `{"model": {"model": "gpt-4.1", "provider": "openai", "maxTokens": 260, "temperature": 0.3, "toolIds": [...]}}` — looked complete, but did NOT include `model.messages`. Result: prompts gone, live calls ran with empty system prompt until the operator forced a restore.
19
+
20
+
**Mandatory workflow for any direct API PATCH against a nested object:**
# 4. Re-GET and verify EVERY field you cared about — not just the one you changed
35
+
```
36
+
37
+
**The "I patched X and X came back correct" check is NOT sufficient.** Vapi can replace the rest of the nested object even when X looks right in the response. Verify the fields you DIDN'T touch survived too — especially `model.messages` (system prompt), `model.toolIds`, `model.knowledgeBase`, and any nested config under `voice` / `transcriber`.
38
+
39
+
**When in doubt, use `npm run push -- <env>` instead of direct API PATCH.** The gitops engine constructs the full payload from local YAML automatically. Only fall back to direct curl PATCH when the engine is silently dropping specific fields (the 2026-04-26 `eagerEotThreshold` engine bug and the 2026-05-13 silent-push class). Even then, GET-modify-PATCH-verify.
40
+
41
+
See `docs/learnings/voice-providers.md` for related "property X should not exist" 400 gotchas (e.g. `voice.enableSsmlParsing` is rejected on `provider: vapi` voices) — those failures are loud; the PATCH-is-REPLACE failure is silent and far more dangerous.
0 commit comments