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
- Point the entry at a concrete object: 'my_callbacks.proxy_handler_instance' (module.instance), not the bare module.
- Verify with python -c "import my_callbacks as m; print(type(m.proxy_handler_instance))" - it must be a CustomLogger instance or function.
- Fix typos in the last path segment; check the attribute actually exists at top level.
- 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
- Always write callbacks entries as module.attribute, never module alone.
- Smoke-test config in a pre-deploy job: import each entry and assert it is an instance/callable.
- Keep callback modules free of debug flags that shadow handler names.
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
- litellm_settings.callbacks entry '{error.entry}' resolved to
- Callback param '{param}' (from {source}) contains an 'os.env
- Internal Error: Cache cannot be empty - internal_usage_cache
- No active custom logger found for callback name: {callback_n
- skip_pre_call_logic=True requires litellm_logging_obj to be
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/37990779766de545.
Report an issue: GitHub.