BerriAI/litellm · error · ValueError
Guardrail {guardrail['guardrail_name']}: scan_only_tool_resu
Error message
Guardrail {guardrail['guardrail_name']}: scan_only_tool_results and skip_tool_message_in_guardrail are enabled together, which excludes every message from scanning, so no request content would ever be scanned. Remove one of the two. What it means
Guardrail init validation: scan_only_tool_results (restrict scanning to tool messages) and skip_tool_message_in_guardrail (exclude tool messages from scanning) are mutually exclusive - together they exclude every message, so no request content would ever be scanned. litellm rejects the combination at startup with this ValueError.
Source
Thrown at litellm/proxy/guardrails/guardrail_registry.py:522
if custom_guardrail_callback is not None:
for scoping_param in (
"skip_system_message_in_guardrail",
"skip_tool_message_in_guardrail",
"scan_only_tool_results",
):
setattr(custom_guardrail_callback, scoping_param, getattr(litellm_params, scoping_param, None))
scan_only_tool_results_enabled: Final = effective_scan_only_tool_results_for_guardrail(
custom_guardrail_callback
)
if scan_only_tool_results_enabled and not custom_guardrail_callback.supports_scan_only_tool_results():
raise ValueError(
f"Guardrail {guardrail['guardrail_name']}: scan_only_tool_results is enabled, but this "
"guardrail's role filtering never scans tool results, so no request content would ever "
"be scanned. Remove scan_only_tool_results or the guardrail's role-filtering option."
)
if scan_only_tool_results_enabled and effective_skip_tool_message_for_guardrail(custom_guardrail_callback):
raise ValueError(
f"Guardrail {guardrail['guardrail_name']}: scan_only_tool_results and "
"skip_tool_message_in_guardrail are enabled together, which excludes every message from "
"scanning, so no request content would ever be scanned. Remove one of the two."
)
configured_run_in_parallel: Final[bool | None] = getattr(litellm_params, "run_in_parallel", None)
if configured_run_in_parallel is not None:
custom_guardrail_callback.run_in_parallel = bool(configured_run_in_parallel)
parsed_guardrail: Final = Guardrail(
guardrail_id=guardrail.get("guardrail_id"),
guardrail_name=guardrail["guardrail_name"],
litellm_params=litellm_params,
guardrail_info=guardrail.get("guardrail_info"),
)
# store references to the guardrail in memory
self.IN_MEMORY_GUARDRAILS[guardrail_id] = parsed_guardrail
self.guardrail_id_to_custom_guardrail[guardrail_id] = custom_guardrail_callbackView on GitHub (pinned to 77b7c6c40c)
Solutions
- Decide the intent: either scan only tool results (keep scan_only_tool_results, drop skip_tool_message_in_guardrail) or exclude tool messages (the reverse)
- Split into two guardrail entries if you need both behaviors for different hooks
- Add a config lint step that rejects guardrail entries containing both flags
Example fix
# before litellm_params: guardrail: aim scan_only_tool_results: true skip_tool_message_in_guardrail: true # after litellm_params: guardrail: aim scan_only_tool_results: true
Defensive patterns
Strategy: validation
Validate before calling
# Hard rule: the two flags are mutually exclusive
import yaml
cfg = yaml.safe_load(open('config.yaml'))
for g in cfg.get('guardrails', []):
lp = g.get('litellm_params') or {}
if lp.get('scan_only_tool_results') and lp.get('skip_tool_message_in_guardrail'):
raise SystemExit(
f"{g.get('guardrail_name')}: pick ONE of scan_only_tool_results / skip_tool_message_in_guardrail"
) Type guard
def has_valid_scoping(litellm_params: dict) -> bool:
"""False when scoping flags would exclude every message from scanning."""
return not (
bool(litellm_params.get('scan_only_tool_results'))
and bool(litellm_params.get('skip_tool_message_in_guardrail'))
) Prevention
- Add a config linter rule for mutually exclusive guardrail flags in CI
- When changing scanning scope, remove obsolete flags instead of adding new ones on top
- Split different scanning intents into separate guardrail entries
When it happens
Trigger: A single guardrail entry with both scan_only_tool_results: true and skip_tool_message_in_guardrail: true in litellm_params.
Common situations: Flag accumulated over time as config evolved - one option added to reduce noise, later the other added for tool-result scanning; merging two guardrail configs into one.
Related errors
- Guardrail {guardrail['guardrail_name']}: scan_only_tool_resu
- guardrail_type is required
- Event hook {hook} is not in the supported event hooks {suppo
- Event hook {event_hook} is not in the supported event hooks
- Cannot set prompt_directory when working with prompt_initial
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/70a4adf798eb1ee7.
Report an issue: GitHub.