ComposioHQ/composio · error · ErrorUploadingFile

Failed to fetch file from URL: {_sanitize_url_for_logging(ur

Error message

Failed to fetch file from URL: {_sanitize_url_for_logging(url)}. Error: {e}

What it means

The legacy top-level preload mechanism received a string instead of a list. preload.tools must be a list of Composio tool slugs or the literal 'all'; custom tools are preloaded by setting preload=True on the tool/toolkit definition, not via preload.tools.

Source

Thrown at python/composio/core/models/_files.py:416

    Raises:
        ResponseTooLargeError: If response exceeds max_size
        ErrorUploadingFile: If fetch fails for other reasons
    """
    # `safe_get` validates the target, connects to the address it validated
    # (so DNS cannot rebind between the two), and never follows redirects.
    try:
        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}"

View on GitHub (pinned to 64b1b85502)

Solutions

  1. Wrap the slug in a list: preload=['my_tool']
  2. Use the literal 'all' (PRELOAD_TOOLS_ALL) only if you truly want everything
  3. Set preload=True on the CustomTool/Toolkit definition instead of using top-level preload for custom tools

Example fix

# before
composio.use(toolkits=['github'], preload_tools='GITHUB_CREATE_ISSUE')
# after
composio.use(toolkits=['github'], preload_tools=['GITHUB_CREATE_ISSUE'])
Defensive patterns

Strategy: type-guard

Validate before calling

if isinstance(preload_tools, str):
    preload_tools = [preload_tools] if preload_tools != 'all' else 'all'
composio.use(toolkits=toolkits, preload_tools=preload_tools)

Type guard

def is_valid_preload(value) -> bool:
    if value is None or value == 'all':
        return True
    return isinstance(value, list) and all(isinstance(s, str) for s in value)

Try / catch

try:
    composio.use(toolkits=toolkits, preload_tools=preload)
except ValidationError as e:
    if 'preload.tools must be a list' in str(e):
        composio.use(toolkits=toolkits, preload_tools=[preload])
    else:
        raise

Prevention

When it happens

Trigger: Passing preload_tools='my_tool' (a bare string) to the SDK path that calls assert_no_custom_tool_slugs_in_preload via _prepare_inline_custom_tools, e.g. composio.use(..., preload={'tools': 'SOME_TOOL'}) or an equivalent string preload argument.

Common situations: Migrating from an older API that accepted a single slug string; writing preload='all' in lowercase or a single slug where a list was expected; copy-pasting a slug where a list belongs.

Understand the failure class

Background: Invalid argument type errors: "must be of type string", "expected X, got Y", and ERR_INVALID_ARG_TYPE explained — this error's family across 15 libraries.

Related errors


AI-assisted analysis of ComposioHQ/composio@64b1b85502 (2026-08-28). Data as JSON: /api/errors/054a8b338575b5ae. Report an issue: GitHub.