ComposioHQ/composio · error · ErrorUploadingFile
URL returned redirect to {_sanitize_url_for_logging(location
Error message
URL returned redirect to {_sanitize_url_for_logging(location)}. Please provide a direct URL to the file. What it means
One or more slugs in the top-level preload.tools list belong to custom tools. Custom tools cannot be preloaded via preload.tools; they must be exposed by setting preload=True on the SDK custom tool or custom toolkit definition.
Source
Thrown at python/composio/core/models/_files.py:424
response = safe_get(
url,
stream=True, # Enable streaming for size limiting
timeout=(_CONNECT_TIMEOUT, _READ_TIMEOUT),
)
except requests.exceptions.Timeout:
raise ErrorUploadingFile(
f"Request timed out fetching URL: {_sanitize_url_for_logging(url)}"
)
except requests.exceptions.RequestException as e:
raise ErrorUploadingFile(
f"Failed to fetch file from URL: {_sanitize_url_for_logging(url)}. Error: {e}"
)
# Reject redirects - require direct URL to resource
if response.status_code in (301, 302, 303, 307, 308):
location = response.headers.get("Location", "unknown")
response.close()
raise ErrorUploadingFile(
f"URL returned redirect to {_sanitize_url_for_logging(location)}. "
f"Please provide a direct URL to the file."
)
# Check for successful response
if not response.ok:
response.close()
raise ErrorUploadingFile(
f"Failed to fetch file from URL: {_sanitize_url_for_logging(url)}. "
f"Status: {response.status_code}"
)
# Check Content-Length header first (early abort for oversized files).
# The header is a hint from the remote server: `parse_content_length`
# returns None for anything untrustworthy, and the streaming guard below
# is the authoritative limit.
content_length = parse_content_length(response.headers.get("Content-Length"))
if content_length is not None and content_length > max_size:View on GitHub (pinned to 64b1b85502)
Solutions
- Remove custom tool slugs from preload.tools and set preload=True on those CustomTool/Toolkit definitions
- Filter the preload list to only Composio app tool slugs
- If you want everything including custom tools, set preload=True on the definitions and use 'all' only for app tools
Example fix
# before tool = CustomTool(slug='MY_TOOL', preload=False, ...) composio.use(custom_tools=[tool], preload_tools=['MY_TOOL']) # after tool = CustomTool(slug='MY_TOOL', preload=True, ...) composio.use(custom_tools=[tool])
Defensive patterns
Strategy: validation
Validate before calling
custom_finals = {f.upper() for f in custom_tools_map.by_final_slug} if custom_tools_map else set()
safe = [s for s in preload_tools if s.upper() not in custom_finals]
composio.use(custom_tools=tools, preload_tools=safe) Type guard
def contains_only_app_tools(preload_tools: list, custom_map) -> bool:
custom_keys = ({*custom_map.by_final_slug} | {*getattr(custom_map, 'by_original_slug', {})}) if custom_map else set()
return all(s.upper() not in {k.upper() for k in custom_keys} for s in preload_tools) Try / catch
try:
composio.use(custom_tools=tools, preload_tools=preload_tools)
except ValidationError as e:
if 'not supported in preload.tools' in str(e):
bad = set(str(e).split(': ')[1].split('.')[0].split(', '))
composio.use(custom_tools=tools, preload_tools=[s for s in preload_tools if s not in bad])
else:
raise Prevention
- Never mix custom tool slugs into preload.tools
- Mark custom tools with preload=True at definition time
When it happens
Trigger: Passing a preload list containing a slug that matches (normalized) a custom tool in custom_tools_map.by_final_slug / by_original_slug during _prepare_inline_custom_tools, e.g. composio.use(preload_tools=['MY_CUSTOM_TOOL']).
Common situations: Treating custom tool slugs like Composio app tool slugs; migrating config where all tools were preloaded from one list; dynamic preload lists built from mixed sources.
Related errors
- Failed to fetch file from URL: {_sanitize_url_for_logging(ur
- Could not determine a home directory to store the Composio c
- Cache directory {directory} is not writable please provide a
- module {__name__!r} has no attribute {name!r}
- Failed to upload to S3: {_sanitize_url_for_logging(url)}. Er
AI-assisted analysis of ComposioHQ/composio@64b1b85502 (2026-08-28).
Data as JSON: /api/errors/4110878aaa4a6ff9.
Report an issue: GitHub.