ComposioHQ/composio · error · ValidationError

Failed to fetch file from URL: {e.status_code} {e.status_tex

Error message

Failed to fetch file from URL: {e.status_code} {e.status_text}

What it means

Raised when the URL fetch for a file upload returns a non-success HTTP status (4xx/5xx). The status code and reason text are included, so auth failures (401/403), missing objects (404), and server errors are distinguishable in the message.

Source

Thrown at python/composio/core/models/tool_router_session_files.py:152

    mimetype = response.headers.get("content-type", "application/octet-stream")
    mimetype = mimetype.split(";")[0].strip()
    return b"".join(chunks), mimetype


def _fetch_from_url(url: str) -> t.Tuple[bytes, str]:
    """Fetch file content from a user-supplied URL. Returns (content, mimetype)."""
    try:
        return _fetch_url_bytes(url)
    except _UrlFetchError as e:
        if e.cause is not None:
            raise ValidationError(f"Failed to fetch file from URL: {e.cause}") from e
        if e.redirected:
            raise ValidationError(
                "URL returned redirect. Please provide a direct URL to the file."
            ) from e
        if e.status_code is not None:
            raise ValidationError(
                f"Failed to fetch file from URL: {e.status_code} {e.status_text}"
            ) from e
        raise ValidationError(str(e)) from e


class RemoteFile:
    """Represents a file stored in a tool router session's file mount.

    Provides methods to fetch, save, and work with the file content.
    """

    def __init__(
        self,
        *,
        expires_at: str,
        mount_relative_path: str,
        sandbox_mount_prefix: str,
        download_url: str,

View on GitHub (pinned to 64b1b85502)

Solutions

  1. Check the code in the message: 403 → regenerate the presigned URL; 404 → object deleted/moved; 429 → back off and retry
  2. Refresh the link generation and re-upload
  3. Fall back to direct byte/path upload if the URL keeps failing

Example fix

# before
files.add(url=expired_presigned_s3_url)

# after
fresh = generate_presigned_url(bucket, key, expires=3600)
files.add(url=fresh)
Defensive patterns

Strategy: retry

Validate before calling

import requests
r = requests.head(url)
assert r.status_code == 200, f'URL returned {r.status_code}'

Try / catch

try:
    files.add(url=url)
except ValidationError as e:
    if '403' in str(e):
        url = refresh_presigned_url(); files.add(url=url)
    else:
        raise

Prevention

When it happens

Trigger: Passing an expired pre-signed URL (403), a deleted file (404), or a link behind authentication (401) to the by-URL upload API.

Common situations: Pre-signed S3/GCS URLs past their expiry, links requiring cookies/login, rate-limited endpoints (429).

Related errors


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