{"record":{"id":"67648e0e76101160","repo":"BerriAI/litellm","slug":"remote-module-loading-s3-gcs-is-only-perm","errorCode":null,"errorMessage":"Remote module loading (s3://, gcs://) is only permitted from the config-file load path. Register the module under ``litellm_settings`` in your config.yaml instead.","messagePattern":"Remote module loading \\(s3://, gcs://\\) is only permitted from the config-file load path\\. Register the module under ``litellm_settings`` in your config\\.yaml instead\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/proxy/types_utils/utils.py","lineNumber":23,"sourceCode":"from collections.abc import Callable\nfrom typing import Any, Final, Literal, get_type_hints\n\n\ndef get_instance_fn(value: str, config_file_path: str | None = None) -> Any:\n    module_name = value\n    instance_name = None\n    try:\n        # Check if value starts with s3:// or gcs://\n        if value.startswith(\"s3://\") or value.startswith(\"gcs://\"):\n            # Remote module loading is a documented operator feature when\n            # invoked from config-file load (``config_file_path`` carries\n            # the YAML path). Without that signal the URL is request-body\n            # data on an admin endpoint — a one-step admin-to-RCE primitive\n            # via ``_load_instance_from_remote_storage``'s ``exec_module``.\n            # Register the module under ``litellm_settings`` in the\n            # config.yaml instead.\n            if config_file_path is None:\n                raise ValueError(\n                    \"Remote module loading (s3://, gcs://) is only \"\n                    \"permitted from the config-file load path. Register \"\n                    \"the module under ``litellm_settings`` in your \"\n                    \"config.yaml instead.\"\n                )\n            return _load_instance_from_remote_storage(value, config_file_path)\n\n        # Split the path by dots to separate module from instance\n        parts: Final = value.split(\".\")\n\n        # The module path is all but the last part, and the instance_name is the last part\n        module_name = \".\".join(parts[:-1])\n        instance_name = parts[-1]\n\n        module_file_path = None\n        if config_file_path is not None:\n            directory: Final = os.path.dirname(config_file_path)\n            module_file_path = os.path.join(directory, *module_name.split(\".\")) + \".py\"","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/types_utils/utils.py#L5-L41","documentation":"Security guard in get_instance_fn: an s3:// or gcs:// module reference was supplied without config_file_path, i.e. from request-body input rather than the trusted config-file load path. Remote code loading from object storage is only permitted for operator-controlled config.yaml values, so the URL is rejected as untrusted input to prevent arbitrary code loading.","triggerScenarios":"Thrown at litellm/proxy/types_utils/utils.py:23 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Move the remote module reference into litellm_settings in your config.yaml instead of loading it from an arbitrary path."],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}