BerriAI/litellm · error · ValueError

litellm_settings.callbacks entry '{error.entry}' resolved to

Error message

litellm_settings.callbacks entry '{error.entry}' resolved to {type(error.loaded).__name__} {error.loaded!r}, which is neither a CustomLogger instance nor a callable, so the proxy would never run it.

What it means

Startup ValueError from callback resolution: the litellm_settings.callbacks entry resolved to an object that is neither a CustomLogger instance, a callable, nor even a class - _CallbackNotDispatchable. The message shows the resolved object's type and repr (e.g. a module, a string constant, an int, a dataclass field). The proxy refuses to start because it would silently never run the callback.

Source

Thrown at litellm/proxy/common_utils/callback_utils.py:103

    if isinstance(loaded, type):
        return _CallbackResolvedToClass(entry=entry, loaded=loaded)
    return _CallbackNotDispatchable(entry=entry, loaded=loaded)


def _raise_callback_load_error(error: _CallbackLoadError) -> NoReturn:
    """The one edge that raises: map a load error onto config load's failure contract."""
    match error:
        case _CallbackResolvedToClass():
            module_path: Final = error.entry.rsplit(".", 1)[0] if "." in error.entry else error.entry
            raise ValueError(
                f"litellm_settings.callbacks entry '{error.entry}' resolved to the class "
                f"{error.loaded.__module__}.{error.loaded.__qualname__}, which is neither a "
                "CustomLogger instance nor a callable, so the proxy would never run it."
                f" Point it at an instance instead, e.g. add `proxy_handler_instance = {error.loaded.__name__}()` to "
                f'{module_path} and set `callbacks: ["{module_path}.proxy_handler_instance"]`.'
            )
        case _CallbackNotDispatchable():
            raise ValueError(
                f"litellm_settings.callbacks entry '{error.entry}' resolved to "
                f"{type(error.loaded).__name__} {error.loaded!r}, which is neither a "
                "CustomLogger instance nor a callable, so the proxy would never run it."
            )
    assert_never(error)


def _loaded_callback_or_raise(entry: str, loaded: object) -> CustomLogger | Callable[..., object]:
    resolved: Final = _classify_loaded_callback(entry=entry, loaded=loaded)
    if isinstance(resolved, _CallbackResolvedToClass | _CallbackNotDispatchable):
        _raise_callback_load_error(resolved)
    return resolved


def initialize_callbacks_on_proxy(
    value: Any,
    premium_user: bool,
    config_file_path: str,

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Point the entry at a concrete object: 'my_callbacks.proxy_handler_instance' (module.instance), not the bare module.
  2. Verify with python -c "import my_callbacks as m; print(type(m.proxy_handler_instance))" - it must be a CustomLogger instance or function.
  3. Fix typos in the last path segment; check the attribute actually exists at top level.
  4. If you meant a class, instantiate it at module level first (see the class-entry error).

Example fix

// before
litellm_settings:
  callbacks: ["my_callbacks"]  # module, not an object

// after
litellm_settings:
  callbacks: ["my_callbacks.proxy_handler_instance"]
Defensive patterns

Strategy: validation

Validate before calling

import importlib

def entry_resolves_to_object(entry: str) -> bool:
    module_name, _, attr = entry.rpartition('.')
    if not attr or not module_name:
        return False  # bare module path
    try:
        return hasattr(importlib.import_module(module_name), attr)
    except ImportError:
        return False

Type guard

def is_valid_callback_entry(entry: object) -> bool:
    if not isinstance(entry, str) or '.' not in entry:
        return False
    module_name, _, attr = entry.rpartition('.')
    try:
        obj = getattr(importlib.import_module(module_name), attr)
    except Exception:
        return False
    import types
    return not isinstance(obj, (types.ModuleType, str, int, float, bool)) and (callable(obj) or hasattr(obj, 'async_post_call_success_hook'))

Prevention

When it happens

Trigger: Pointing callbacks at a module path ('my_callbacks') instead of an attribute inside it; referencing a module-level variable (DEBUG = True), an imported name that is itself a module, or a typo'd attribute that resolves via importlib to something unexpected.

Common situations: Missing the attribute after the last dot; env-conditional definitions where the name is a flag not a logger; packages that expose submodules shadowing intended objects; copy-paste paths from another project whose module layout differs.

Related errors


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