BerriAI/litellm · error · CustomCodeCompilationError

Custom code must define an 'apply_guardrail' function. Expec

Error message

Custom code must define an 'apply_guardrail' function. Expected signature: apply_guardrail(inputs, request_data, input_type)

What it means

CustomCodeCompilationError raised after the sandboxed custom code compiled and executed but no top-level apply_guardrail name exists in the execution globals. The custom-code guardrail contract requires an entry point with signature apply_guardrail(inputs, request_data, input_type); syntax-valid code that never defines it (or misspells it) fails here.

Source

Thrown at litellm/proxy/guardrails/guardrail_hooks/custom_code/custom_code_guardrail.py:156

    @classmethod
    def get_supported_event_hooks(cls) -> list[GuardrailEventHooks]:
        return [
            GuardrailEventHooks.pre_call,
            GuardrailEventHooks.during_call,
            GuardrailEventHooks.post_call,
            GuardrailEventHooks.pre_mcp_call,
            GuardrailEventHooks.during_mcp_call,
            GuardrailEventHooks.logging_only,
        ]

    def _do_compile(self) -> None:
        """Internal compilation method without lock. Expected to run inside _compile_lock."""
        exec_globals: Final = build_sandbox_globals()
        compiled: Final = compile_sandboxed(self.custom_code)
        exec(compiled, exec_globals)  # noqa: S102

        if "apply_guardrail" not in exec_globals:
            raise CustomCodeCompilationError(
                "Custom code must define an 'apply_guardrail' function. "
                "Expected signature: apply_guardrail(inputs, request_data, input_type)"
            )

        apply_fn: Final = exec_globals["apply_guardrail"]
        if not callable(apply_fn):
            raise CustomCodeCompilationError("'apply_guardrail' must be a callable function")

        self._compiled_function = apply_fn

    def _compile_custom_code(self) -> None:
        """
        Compile the custom code and extract the apply_guardrail function.

        The code runs in a sandboxed environment with only the allowed primitives.
        """
        with self._compile_lock:
            if self._compiled_function is not None:

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Define def apply_guardrail(inputs, request_data, input_type): at the top level of custom_code
  2. Check the exact spelling — singular, underscore-separated, case-sensitive
  3. Ensure every path in the function returns allow()/block(...)/modify(...) rather than falling through

Example fix

# before: wrong entry-point name
def check_prompts(inputs):
    return allow()

# after
def apply_guardrail(inputs, request_data, input_type):
    for text in inputs.get('texts') or []:
        if 'secret-project' in text:
            return block('confidential term detected')
    return allow()
Defensive patterns

Strategy: validation

Validate before calling

from litellm.proxy.guardrails.guardrail_hooks.custom_code.sandbox import (
    build_sandbox_globals, compile_sandboxed,
)

def preflight(custom_code: str) -> None:
    g = build_sandbox_globals()
    exec(compile_sandboxed(custom_code), g)
    assert callable(g.get('apply_guardrail')), 'custom_code must define callable apply_guardrail(inputs, request_data, input_type)'

Type guard

def has_entrypoint(exec_globals: dict) -> bool:
    return callable(exec_globals.get('apply_guardrail'))

Try / catch

from litellm.proxy.guardrails.guardrail_hooks.custom_code.custom_code_guardrail import CustomCodeCompilationError
try:
    guardrail = CustomCodeGuardrail(guardrail_name='g', custom_code=src, event_hook='pre_call', default_on=True)
    guardrail._compile_custom_code()
except CustomCodeCompilationError as e:
    raise SystemExit(f'custom_code rejected: {e}') from e

Prevention

When it happens

Trigger: custom_code defines the function under a different name (apply_guardrails plural, run_guardrail, check) or only defines helpers/constants; raised on first compile — at guardrail instantiation or on the first hooked request.

Common situations: Porting snippets from other frameworks with a different entry-point name; refactoring renames the function; generated code emits a differently named wrapper.

Related errors


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/3d8b06fadff9cb89. Report an issue: GitHub.