BerriAI/litellm · error · CustomCodeCompilationError

Failed to compile custom code: {e}

Error message

Failed to compile custom code: {e}

What it means

CustomCodeCompilationError raised when compiling/executing the sandboxed code raises anything other than a SyntaxError or a prior CustomCodeCompilationError — typically a runtime error thrown while exec() runs module-level statements, since compilation happens in two phases (compile, then exec in sandbox globals). The original exception is chained via raise ... from e and its message is embedded.

Source

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

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

            try:
                self._do_compile()
                verbose_proxy_logger.debug("Custom code guardrail '%s' compiled successfully", self.guardrail_name)

            except SyntaxError as e:
                self._compile_error = f"Syntax error in custom code: {e}"
                raise CustomCodeCompilationError(self._compile_error) from e
            except CustomCodeCompilationError:
                raise
            except Exception as e:
                self._compile_error = f"Failed to compile custom code: {e}"
                raise CustomCodeCompilationError(self._compile_error) from e

    @log_guardrail_information
    async def apply_guardrail(
        self,
        inputs: GenericGuardrailAPIInputs,
        request_data: dict,
        input_type: Literal["request", "response"],
        logging_obj: Optional["LiteLLMLoggingObj"] = None,
    ) -> GenericGuardrailAPIInputs:
        """
        Apply the custom code guardrail to the inputs.

        This method calls the user-defined apply_guardrail function and
        processes its result to determine the appropriate action.

        The user-defined function can be either sync or async:
        - Sync: def apply_guardrail(inputs, request_data, input_type): ...
        - Async: async def apply_guardrail(inputs, request_data, input_type): ...

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Read the embedded {e} — it names the exact exception and expression that failed during exec
  2. Move all logic inside def apply_guardrail; keep the module top level to declarations only
  3. Remove import statements — use only the primitives the sandbox provides (regex_match, http_get, http_post, block, allow, ...)
  4. Defer any expensive or environment-dependent computation to first call inside the entry function

Example fix

# before: import-time work fails inside the sandbox
import os
FLAG = os.environ.get('STRICT')

def apply_guardrail(inputs, request_data, input_type):
    ...

# after: declarative top level, lazy reads inside the entry point
def apply_guardrail(inputs, request_data, input_type):
    strict = (request_data.get('metadata') or {}).get('strict', False)
    if strict:
        return block('strict mode')
    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 sandbox_preflight(custom_code: str) -> None:
    g = build_sandbox_globals()
    try:
        exec(compile_sandboxed(custom_code), g)
    except Exception as e:
        raise ValueError(f'custom_code fails inside the sandbox: {type(e).__name__}: {e}') from e
    assert callable(g.get('apply_guardrail'))

Try / catch

from litellm.proxy.guardrails.guardrail_hooks.custom_code.custom_code_guardrail import CustomCodeCompilationError
try:
    guardrail._compile_custom_code()
except CustomCodeCompilationError as e:
    cause = e.__cause__  # the original module-level failure
    raise SystemExit(f'custom_code rejected: {e} (caused by {cause!r})') from e

Prevention

When it happens

Trigger: Module-level code that executes during sandbox exec and fails: calling a bounds-checked limited builtin out of range (e.g. limited_range with a huge bound), NameError from an undefined name, TypeError while evaluating a class body, or accessing attributes the sandbox denies at exec time.

Common situations: Trying to import packages at module top (import os fails — the sandbox provides no __import__); module-level config reads or heavy computation that runs at import; code copied from a normal Python module that does work on import.

Related errors


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