Skip to content

Bug: Azure content filter rejections open the circuit breaker, failing unrelated compressions #1276

Description

@dKaulig

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions