Observed in a FastAPI + litellm/Anthropic backend whose LLM tests replay VCR cassettes (pytest-recording, record_mode none in CI).
I changed three prompts: added a length target to an extraction prompt, a length target to a fact-check prompt and to its tool description. I ran the full suite before re-recording anything and got 158 passed. The cassettes still held the old prompt text in their request bodies; nothing noticed.
Why: vcrpy's default match_on is ['method', 'scheme', 'host', 'port', 'path', 'query']. LLM APIs put the entire prompt in the POST body and use a fixed path (/v1/messages, /v1beta/models/...:generateContent), so every request to the same model matches the first recorded interaction in order. A prompt change, a schema change, or even a model-param change replays stale output. Assertions on the stale output still pass, so the suite gives you false confidence that the new prompt works.
What to do:
- Treat "prompt text changed" as "these cassettes must be re-recorded": find them by grepping cassette YAML for a phrase from the old prompt (the request body is stored verbatim), then run just those tests with
--record-mode=rewriteand read the new model output, not just the assertions. - If you want the suite to fail on drift instead, add a custom matcher on a hash of the system prompt (not the raw body: bodies often carry timestamps, trace ids, or base64 audio that change per run).
vcr.register_matcher('prompt', lambda r1, r2: ...)and add it tomatch_on. - Beware that
match_on=['body']with multimodal requests (inline base64 audio/images) makes cassettes huge-diff and brittle; hash only the text parts.
Verified: after re-recording four cassettes the new outputs honored the new targets (names <= 40 chars, notes 83-98 chars), which the stale replays could never have shown.