Summary
ResilientProvider counts every thrown error as a provider failure, including content filter rejections. Azure OpenAI content filters reject individual prompts, so three filtered observations inside the 60s failure window open the circuit breaker — and then every other compression fails with circuit_breaker_open for the 30s recovery timeout. A single awkward file takes an unrelated batch of observations down with it.
Why this is not a rare edge case
Azure's Prompt Shields flags input that looks like a jailbreak attempt. A memory system that compresses arbitrary tool output feeds it exactly that kind of material without any of it being an attack.
Measured against a gpt-5.4-mini deployment with the default content filter, using the real prompt shape that buildCompressionPrompt() produces:
| Payload |
Result |
| 6 KB of real source code |
200 — protected-material for code does not fire |
error log containing kill / fatal / attack |
200 |
| credentials in tool output |
200 |
| tool output containing injection-style text |
400 content_filter |
a SECURITY.md that merely describes prompt injection |
400 content_filter |
The last row is the point: legitimate documentation is enough. Any repo with a page about prompt security produces these.
The rejection is unambiguous — every severity category is safe, only the jailbreak shield fires:
{"error":{"code":"content_filter","status":400,
"innererror":{"code":"ResponsibleAIPolicyViolation",
"content_filter_result":{
"hate":{"filtered":false,"severity":"safe"},
"jailbreak":{"detected":true,"filtered":true},
"self_harm":{"filtered":false,"severity":"safe"},
"sexual":{"filtered":false,"severity":"safe"},
"violence":{"filtered":false,"severity":"safe"}}}}}
Reproduction
With AGENTMEMORY_AUTO_COMPRESS=true against an Azure OpenAI deployment with default content filtering, POST six observations to /agentmemory/observe — five whose tool_output contains injection-style text, then one harmless.
Observed in stderr.log:
Compression failed {"obsId":"…","error":"OpenAI API error (400): …content_filter…"} ×3
Compression failed {"obsId":"…","error":"circuit_breaker_open"} ×2
Compression failed {"obsId":"…","error":"circuit_breaker_open"} ← the harmless one
Expected: five failures and one successful compression. The sixth observation has nothing wrong with it.
Two things worth knowing when reproducing this:
- The hook field is
tool_output. A payload using a different key is silently dropped, the prompt never reaches the provider, and the run looks like a pass.
- Observation dedup hashes
(sessionId, toolName, toolInput) in memory, so a second run with the same command returns {"deduplicated": true} without calling the provider. Vary the session id and the command per run.
Expected behaviour
A content filter rejection describes the payload, not the health of the provider. The call should fail; the breaker should stay closed so unrelated observations keep compressing.
Environment
- agentmemory 0.9.29
- Node 24.19.0, macOS 26.6 (arm64)
- Provider: Azure OpenAI (
gpt-5.4-mini deployment), default content filter configuration
AGENTMEMORY_AUTO_COMPRESS=true, GRAPH_EXTRACTION_ENABLED=true, CONSOLIDATION_ENABLED=true
Happy to send a PR — I have the fix and tests ready.
Summary
ResilientProvidercounts every thrown error as a provider failure, including content filter rejections. Azure OpenAI content filters reject individual prompts, so three filtered observations inside the 60s failure window open the circuit breaker — and then every other compression fails withcircuit_breaker_openfor the 30s recovery timeout. A single awkward file takes an unrelated batch of observations down with it.Why this is not a rare edge case
Azure's Prompt Shields flags input that looks like a jailbreak attempt. A memory system that compresses arbitrary tool output feeds it exactly that kind of material without any of it being an attack.
Measured against a
gpt-5.4-minideployment with the default content filter, using the real prompt shape thatbuildCompressionPrompt()produces:kill/fatal/attackcontent_filterSECURITY.mdthat merely describes prompt injectioncontent_filterThe last row is the point: legitimate documentation is enough. Any repo with a page about prompt security produces these.
The rejection is unambiguous — every severity category is
safe, only the jailbreak shield fires:{"error":{"code":"content_filter","status":400, "innererror":{"code":"ResponsibleAIPolicyViolation", "content_filter_result":{ "hate":{"filtered":false,"severity":"safe"}, "jailbreak":{"detected":true,"filtered":true}, "self_harm":{"filtered":false,"severity":"safe"}, "sexual":{"filtered":false,"severity":"safe"}, "violence":{"filtered":false,"severity":"safe"}}}}}Reproduction
With
AGENTMEMORY_AUTO_COMPRESS=trueagainst an Azure OpenAI deployment with default content filtering, POST six observations to/agentmemory/observe— five whosetool_outputcontains injection-style text, then one harmless.Observed in
stderr.log:Expected: five failures and one successful compression. The sixth observation has nothing wrong with it.
Two things worth knowing when reproducing this:
tool_output. A payload using a different key is silently dropped, the prompt never reaches the provider, and the run looks like a pass.(sessionId, toolName, toolInput)in memory, so a second run with the same command returns{"deduplicated": true}without calling the provider. Vary the session id and the command per run.Expected behaviour
A content filter rejection describes the payload, not the health of the provider. The call should fail; the breaker should stay closed so unrelated observations keep compressing.
Environment
gpt-5.4-minideployment), default content filter configurationAGENTMEMORY_AUTO_COMPRESS=true,GRAPH_EXTRACTION_ENABLED=true,CONSOLIDATION_ENABLED=trueHappy to send a PR — I have the fix and tests ready.