{"record":{"id":"4110878aaa4a6ff9","repo":"ComposioHQ/composio","slug":"url-returned-redirect-to-sanitize-url-for-loggin","errorCode":null,"errorMessage":"URL returned redirect to {_sanitize_url_for_logging(location)}. Please provide a direct URL to the file.","messagePattern":"URL returned redirect to (.+?)\\. Please provide a direct URL to the file\\.","errorType":"exception","errorClass":"ErrorUploadingFile","httpStatus":null,"severity":"error","filePath":"python/composio/core/models/_files.py","lineNumber":424,"sourceCode":"        response = safe_get(\n            url,\n            stream=True,  # Enable streaming for size limiting\n            timeout=(_CONNECT_TIMEOUT, _READ_TIMEOUT),\n        )\n    except requests.exceptions.Timeout:\n        raise ErrorUploadingFile(\n            f\"Request timed out fetching URL: {_sanitize_url_for_logging(url)}\"\n        )\n    except requests.exceptions.RequestException as e:\n        raise ErrorUploadingFile(\n            f\"Failed to fetch file from URL: {_sanitize_url_for_logging(url)}. Error: {e}\"\n        )\n\n    # Reject redirects - require direct URL to resource\n    if response.status_code in (301, 302, 303, 307, 308):\n        location = response.headers.get(\"Location\", \"unknown\")\n        response.close()\n        raise ErrorUploadingFile(\n            f\"URL returned redirect to {_sanitize_url_for_logging(location)}. \"\n            f\"Please provide a direct URL to the file.\"\n        )\n\n    # Check for successful response\n    if not response.ok:\n        response.close()\n        raise ErrorUploadingFile(\n            f\"Failed to fetch file from URL: {_sanitize_url_for_logging(url)}. \"\n            f\"Status: {response.status_code}\"\n        )\n\n    # Check Content-Length header first (early abort for oversized files).\n    # The header is a hint from the remote server: `parse_content_length`\n    # returns None for anything untrustworthy, and the streaming guard below\n    # is the authoritative limit.\n    content_length = parse_content_length(response.headers.get(\"Content-Length\"))\n    if content_length is not None and content_length > max_size:","sourceCodeStart":406,"sourceCodeEnd":442,"githubUrl":"https://github.com/ComposioHQ/composio/blob/64b1b85502b1beeb2379e6c9e8bf1104504fa637/python/composio/core/models/_files.py#L406-L442","documentation":"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.","triggerScenarios":"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']).","commonSituations":"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.","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"],"exampleFix":"# before\ntool = CustomTool(slug='MY_TOOL', preload=False, ...)\ncomposio.use(custom_tools=[tool], preload_tools=['MY_TOOL'])\n# after\ntool = CustomTool(slug='MY_TOOL', preload=True, ...)\ncomposio.use(custom_tools=[tool])","handlingStrategy":"validation","validationCode":"custom_finals = {f.upper() for f in custom_tools_map.by_final_slug} if custom_tools_map else set()\nsafe = [s for s in preload_tools if s.upper() not in custom_finals]\ncomposio.use(custom_tools=tools, preload_tools=safe)","typeGuard":"def contains_only_app_tools(preload_tools: list, custom_map) -> bool:\n    custom_keys = ({*custom_map.by_final_slug} | {*getattr(custom_map, 'by_original_slug', {})}) if custom_map else set()\n    return all(s.upper() not in {k.upper() for k in custom_keys} for s in preload_tools)","tryCatchPattern":"try:\n    composio.use(custom_tools=tools, preload_tools=preload_tools)\nexcept ValidationError as e:\n    if 'not supported in preload.tools' in str(e):\n        bad = set(str(e).split(': ')[1].split('.')[0].split(', '))\n        composio.use(custom_tools=tools, preload_tools=[s for s in preload_tools if s not in bad])\n    else:\n        raise","preventionTips":["Never mix custom tool slugs into preload.tools","Mark custom tools with preload=True at definition time"],"tags":["python","custom-tools","preload","validation"],"backgroundTag":"unsupported-configuration-value","analyzedSha":"64b1b85502b1beeb2379e6c9e8bf1104504fa637","analyzedAt":"2026-08-28T15:39:33.623Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}