{"record":{"id":"0789e51e4808bdc9","repo":"ComposioHQ/composio","slug":"failed-to-upload-to-s3-status-response-status-c","errorCode":null,"errorMessage":"Failed to upload to S3. Status: {response.status_code}. This may indicate an expired presigned URL or permission issue.","messagePattern":"Failed to upload to S3\\. Status: (.+?)\\. This may indicate an expired presigned URL or permission issue\\.","errorType":"exception","errorClass":"ErrorUploadingFile","httpStatus":null,"severity":"error","filePath":"python/composio/core/models/_files.py","lineNumber":228,"sourceCode":"    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\n            presigned URL was requested with, otherwise S3 rejects the PUT\n            with ``403 SignatureDoesNotMatch`` when the signature covers the\n            content type.\n\n    Returns:","sourceCodeStart":210,"sourceCodeEnd":246,"githubUrl":"https://github.com/ComposioHQ/composio/blob/64b1b85502b1beeb2379e6c9e8bf1104504fa637/python/composio/core/models/_files.py#L210-L246","documentation":"While building the custom tools map from a response, two top-level custom tool handles share the same slug (compared case-insensitively via .upper()). Every custom tool must have a unique slug across all custom tools and toolkits.","triggerScenarios":"Passing a tools list to build_custom_tools_map_from_response (via create/use) containing two CustomTool handles whose slug.upper() matches.","commonSituations":"Concatenating tool lists that each define the same tool; copy-pasting tool definitions; case-variant slugs like 'Send_Email' and 'send_email'.","solutions":["Dedupe the tools list before passing it (by slug.upper())","Rename one of the duplicate-slug tools"],"exampleFix":"# before\ntools = [t1, t2]  # both slug='SEND'\n# after\nseen = set()\ntools = [t for t in [t1, t2] if not (t.slug.upper() in seen or seen.add(t.slug.upper()))]","handlingStrategy":"validation","validationCode":"keys = [h.slug.upper() for h in tools]\nif len(keys) != len(set(keys)):\n    raise ValueError(f'Duplicate tool slugs: {sorted({k for k in keys if keys.count(k) > 1})}')\nresult = build_custom_tools_map_from_response(tools, toolkits)","typeGuard":"def unique_tool_slugs(tools: list) -> bool:\n    keys = [h.slug.upper() for h in tools]\n    return len(keys) == len(set(keys))","tryCatchPattern":"try:\n    m = build_custom_tools_map_from_response(tools, toolkits)\nexcept ValidationError as e:\n    if 'unique slug' in str(e):\n        seen, deduped = set(), []\n        for h in tools:\n            k = h.slug.upper()\n            if k not in seen:\n                seen.add(k)\n                deduped.append(h)\n        m = build_custom_tools_map_from_response(deduped, toolkits)\n    else:\n        raise","preventionTips":["Dedupe tool lists by slug.upper() before merging lists from modules","Standardize on one casing convention for slugs"],"tags":["python","custom-tools","slug","duplicate","validation"],"backgroundTag":"duplicate-identifier","analyzedSha":"64b1b85502b1beeb2379e6c9e8bf1104504fa637","analyzedAt":"2026-08-28T15:39:33.623Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}