ComposioHQ/composio · error · Error

Failed to upload file: ${uploadResponse.statusText}

Error message

Failed to upload file: ${uploadResponse.statusText}

What it means

After obtaining a presigned upload URL, the SDK performs a raw fetch PUT of the file bytes. If the storage endpoint responds with a non-2xx status, a generic Error with the response statusText is thrown. This usually indicates the presigned URL was rejected, expired, or the request was malformed at the object-storage layer, not the Composio API layer.

Source

Thrown at ts/packages/core/src/models/ToolRouterSessionFileMount.ts:258

        ? (createUploadURLResponse as { body: unknown }).body
        : createUploadURLResponse;

    // SSRF guard: `upload_url` comes from the API response, so the target is
    // validated before the file's bytes are sent to it — the same treatment the
    // user-supplied URL above already gets. See ssrfGuard.node.ts.
    const uploadResponse = await ssrfSafeFetchWhereSupported(
      (uploadURLData as { upload_url: string }).upload_url,
      {
        method: 'PUT',
        body: await fileToUpload.arrayBuffer(),
        headers: {
          'Content-Type': mimetype,
        },
      }
    );

    if (!uploadResponse.ok) {
      throw new Error(`Failed to upload file: ${uploadResponse.statusText}`);
    }

    const createDownloadURLResponse = await this.client.toolRouter.session.files.createDownloadURL(
      uploadOptions.data.mountId,
      {
        session_id: this.sessionId,
        mount_relative_path: (uploadURLData as { mount_relative_path: string }).mount_relative_path,
      }
    );

    const downloadData =
      typeof createDownloadURLResponse === 'object' && 'body' in createDownloadURLResponse
        ? (createDownloadURLResponse as { body: unknown }).body
        : createDownloadURLResponse;

    const parsed = RemoteFileDataSchema.safeParse(downloadData);
    if (!parsed.success) {
      throw new ValidationError('Failed to parse remote file properties', {

View on GitHub (pinned to 64b1b85502)

Solutions

  1. Retry the whole upload call so a fresh presigned URL is generated
  2. Ensure mimetype passed at upload matches what the URL was signed for (do not modify headers)
  3. Check network/proxy settings if uploads consistently fail

Example fix

// before
const remote = await mount.upload(file);
// after
let remote;
for (let i = 0; i < 3; i++) {
  try { remote = await mount.upload(file); break; }
  catch (e) { if (i === 2) throw e; await new Promise(r => setTimeout(r, 1000 * (i + 1))); }
}
Defensive patterns

Strategy: retry

Try / catch

try { await mount.upload(file, opts); } catch (e) { if (e instanceof Error && e.message.startsWith('Failed to upload file:')) { /* retry with fresh URL */ } throw e; }

Prevention

When it happens

Trigger: PUT to the presigned URL returns non-ok: expired presigned URL (long delay between createUploadURL and the PUT), Content-Type mismatch with the signed headers, network/proxy interference, or corrupted/empty body.

Common situations: Slow networks where the upload starts after URL expiry, corporate proxies rewriting requests, retries that reuse a stale presigned URL, or a mismatched mimetype between signing and upload.

Related errors


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