{"record":{"id":"dbe79ffbcac40c42","repo":"ComposioHQ/composio","slug":"failed-to-upload-to-s3-sanitize-url-for-logging","errorCode":null,"errorMessage":"Failed to upload to S3: {_sanitize_url_for_logging(url)}. Error: {type(e).__name__}","messagePattern":"Failed to upload to S3: (.+?)\\. Error: (.+?)","errorType":"exception","errorClass":"ErrorUploadingFile","httpStatus":null,"severity":"error","filePath":"python/composio/core/models/_files.py","lineNumber":223,"sourceCode":"    Routing every presigned PUT through one helper keeps the file and bytes\n    upload paths from drifting apart again, mirroring ``uploadFileToS3`` in\n    the TypeScript SDK, which funnels path, URL, and File inputs through a\n    single uploader.\n\n    Raises:\n        ErrorUploadingFile: On transport failure or a non-200 response,\n            including the HTTP status when one was received.\n    \"\"\"\n    try:\n        response = safe_request(\n            \"PUT\",\n            url,\n            data=data,\n            headers={\"Content-Type\": mimetype},\n            timeout=(_CONNECT_TIMEOUT, _READ_TIMEOUT),\n        )\n    except requests.exceptions.RequestException as e:\n        raise ErrorUploadingFile(\n            \"Failed to upload to S3: \"\n            f\"{_sanitize_url_for_logging(url)}. Error: {type(e).__name__}\"\n        ) from e\n    if response.status_code != 200:\n        raise ErrorUploadingFile(\n            f\"Failed to upload to S3. Status: {response.status_code}. \"\n            \"This may indicate an expired presigned URL or permission issue.\"\n        )\n\n\ndef upload(url: str, file: Path, mimetype: t.Optional[str] = None) -> bool:\n    \"\"\"Upload file to presigned S3 URL.\n\n    Args:\n        url: Presigned S3 upload URL\n        file: Path to file to upload\n        mimetype: Content type to send with the upload. Defaults to the type\n            guessed from ``file``. This must match the ``mimetype`` the","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/ComposioHQ/composio/blob/64b1b85502b1beeb2379e6c9e8bf1104504fa637/python/composio/core/models/_files.py#L205-L241","documentation":"One original slug maps to multiple different final slugs, which would make tool resolution ambiguous. This happens when the same custom tool slug appears in contexts that produce different final slugs (e.g. the same bare slug both inside and outside a prefixed toolkit).","triggerScenarios":"Registering a custom tool with slug X standalone and the same slug X inside a toolkit whose prefix produces a different final slug, so by_original_slug[X] already has an entry with a different final_slug, during build_custom_tools_map(_from_response).","commonSituations":"Mixing standalone tools and toolkit tools that share slugs; refactoring tools into toolkits while leaving standalone copies registered; dynamically composed tool lists.","solutions":["Remove the standalone copy of the tool that is also inside the toolkit","Rename one instance so each original slug appears in only one context","Audit how toolkits are combined before calling create/use"],"exampleFix":"# before\ntools = [CustomTool(slug='FETCH', ...)]\ntoolkit = Toolkit(slug='gh', tools=[CustomTool(slug='FETCH', ...)])\n# after\ntools = []\ntoolkit = Toolkit(slug='gh', tools=[CustomTool(slug='FETCH', ...)])","handlingStrategy":"validation","validationCode":"standalone = {h.slug.upper() for h in tools}\nin_toolkits = {h.slug.upper() for tk in toolkits or [] for h in tk.tools}\noverlap = standalone & in_toolkits\nif overlap:\n    raise ValueError(f'Slugs registered both standalone and in toolkits: {overlap}')","typeGuard":"def each_slug_single_context(tools: list, toolkits: list) -> bool:\n    standalone = {h.slug.upper() for h in tools}\n    in_tk = {h.slug.upper() for tk in toolkits for h in tk.tools}\n    return not (standalone & in_tk)","tryCatchPattern":"try:\n    tools_map = build_custom_tools_map(tools, toolkits)\nexcept ValidationError as e:\n    if 'maps to multiple final slugs' in str(e):\n        tools = [h for h in tools if h.slug.upper() not in toolkit_slugs(toolkits)]\n        tools_map = build_custom_tools_map(tools, toolkits)\n    else:\n        raise","preventionTips":["Register each tool in exactly one place: standalone OR in a toolkit","When moving tools into toolkits, delete the old standalone registrations"],"tags":["python","custom-tools","slug","collision","validation"],"backgroundTag":"ambiguous-identifier-mapping","analyzedSha":"64b1b85502b1beeb2379e6c9e8bf1104504fa637","analyzedAt":"2026-08-28T15:39:33.623Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}