BerriAI/litellm · error · HTTPException
Blocked by custom code guardrail
Error message
Blocked by custom code guardrail
What it means
HTTP 400 raised by the custom-code guardrail when the user function's verdict is a block on a response (post-call): detail carries the block reason ('Blocked by custom code guardrail' unless a custom reason was given), the guardrail name, and detection_info. The same verdict pre-call instead raises a passthrough exception that returns a synthetic response to the client, so this 400 shape is specifically the post-call path.
Source
Thrown at litellm/proxy/guardrails/guardrail_hooks/custom_code/custom_code_guardrail.py:342
reason: Final = result.get("reason", "Blocked by custom code guardrail")
detection_info: Final = result.get("detection_info", {})
verbose_proxy_logger.info(
"Custom code guardrail '%s': Blocking %s - %s", self.guardrail_name, input_type, reason
)
is_output: Final = input_type == "response"
# For pre-call, raise passthrough exception to return synthetic response
if not is_output:
self.raise_passthrough_exception(
violation_message=reason,
request_data=request_data,
detection_info=detection_info,
)
# For post-call, raise HTTP exception
raise HTTPException(
status_code=400,
detail={
"error": reason,
"guardrail": self.guardrail_name,
"detection_info": detection_info,
},
)
elif action == "modify":
verbose_proxy_logger.debug("Custom code guardrail '%s': Modifying %s", self.guardrail_name, input_type)
# Apply modifications
modified_inputs: Final = dict(inputs)
if "texts" in result and result["texts"] is not None:
modified_inputs["texts"] = result["texts"]
if "images" in result and result["images"] is not None:View on GitHub (pinned to 77b7c6c40c)
Solutions
- Treat as expected behavior: surface detail.error and detail.detection_info to the caller; do not retry — the block is deterministic for the same output
- Tune the custom rule if it over-blocks (widen allow patterns, narrow the regex)
- Move the check to a pre-call hook if you want a synthetic response instead of a post-call 400
- Use the block reason string (block('reason')) so clients get an actionable message instead of the generic default
Example fix
# custom_code: before — default generic reason
return block()
# custom_code: after — actionable reason
return block('Response contained a customer account number')
# caller: handle the 400 explicitly
try:
out = await client.chat.completions.create(**params)
except Exception as e:
body = getattr(getattr(e, 'response', None), 'json', lambda: {})() or {}
err = body.get('error', {})
if err and body.get('guardrail') == 'my-custom-guardrail':
return blocked_to_user(err, body.get('detection_info'))
raise Defensive patterns
Strategy: try-catch
Try / catch
try:
out = await client.chat.completions.create(**params)
except Exception as e:
body = getattr(getattr(e, 'response', None), 'json', lambda: {})() or {}
err = body.get('error')
if isinstance(err, str) and body.get('guardrail'):
return blocked_to_user(err, body.get('detection_info')) # verdict, not a bug; no retry
raise Prevention
- Give block(...) a specific reason string so downstream handlers can branch on it
- Remember the pre-call path returns a synthetic response while post-call raises 400 - pick the hook that matches your UX
- Do not put guardrail 400s into retry policies; they are deterministic for identical outputs
- Return detection_info to internal callers for audit trails, but sanitize it before showing end users
When it happens
Trigger: apply_guardrail returns block(...) while input_type == 'response' — the model output matched the custom rule (e.g. PII leaked in a completion, or a response-rejection phrase template); the post-call hook converts the verdict to a 400 for the caller.
Common situations: Output filters for refusal phrases or PII in completions (including the shipped RESPONSE_REJECTION_GUARDRAIL_CODE); clients see a 400 whose detail.guardrail names the custom guardrail and mistake it for a bug rather than a verdict.
Related errors
- Violated CrowdStrike AIDR guardrail policy
- Violated Lasso guardrail policy
- blocked_pii_entity
- guardrail_violation
- Blocked by Cisco AI Defense Guardrail
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/3a7dabc21a174dfc.
Report an issue: GitHub.