ComposioHQ/composio · error · RemoteFileDownloadError
Failed to download file: ${response.status} ${response.statu
Error message
Failed to download file: ${response.status} ${response.statusText} What it means
The HTTP fetch of the file's downloadUrl returned a non-2xx status, and RemoteFile.buffer() wraps it in RemoteFileDownloadError with status, statusText, downloadUrl and mountRelativePath attached. The URL is fetched through an SSRF-safe wrapper because it comes from an API response.
Source
Thrown at ts/packages/core/src/models/RemoteFile.ts:91
}
/** Filename extracted from the mount path (e.g. "report.pdf" from "output/report.pdf") */
get filename(): string {
return platform.basename(this.mountRelativePath);
}
/**
* Fetches the file content as a buffer.
* @returns The file content as a Uint8Array
* @throws RemoteFileDownloadError if the fetch fails
*/
async buffer(): Promise<Uint8Array> {
// SSRF guard: `downloadUrl` is set from an API response, so it is untrusted
// input like every other response field, and its bytes are handed straight
// back to the caller. See ssrfGuard.node.ts.
const response = await ssrfSafeFetchWhereSupported(this.downloadUrl);
if (!response.ok) {
throw new RemoteFileDownloadError(
`Failed to download file: ${response.status} ${response.statusText}`,
{
statusCode: response.status,
statusText: response.statusText,
downloadUrl: this.downloadUrl,
mountRelativePath: this.mountRelativePath,
filename: this.filename,
cause: new Error(`HTTP ${response.status}: ${response.statusText}`),
}
);
}
const arrayBuffer = await response.arrayBuffer();
return new Uint8Array(arrayBuffer);
}
/**
* Fetches the file content as UTF-8 text.
* @returns The file content as a stringView on GitHub (pinned to 64b1b85502)
Solutions
- Retry with a freshly fetched RemoteFile (re-list or re-fetch the parent resource to get a new signed URL)
- Check err.statusCode on RemoteFileDownloadError: 403/410 usually means expired link, 404 deleted file
- If 5xx, retry with backoff after re-fetching the file record
Example fix
// before const bytes = await file.buffer(); // stale signed URL // after const fresh = await composio.sessions.get(sessionId).files.get(fileId); const bytes = await fresh.buffer();
Defensive patterns
Strategy: retry
Validate before calling
if (!file.downloadUrl) throw new Error('no download URL; refetch file record'); Try / catch
try { const bytes = await file.buffer(); } catch (e) {
if (e instanceof RemoteFileDownloadError) {
if ([403, 404, 410].includes(e.statusCode)) { const fresh = await refetchFile(file.id); return fresh.buffer(); }
await sleep(backoff); return file.buffer();
}
throw e;
} Prevention
- Download promptly after fetching a RemoteFile
- Refetch the file record to refresh signed URLs instead of caching them
- Branch on statusCode to distinguish expiry from transient failures
When it happens
Trigger: Calling await remoteFile.buffer() (or any API that internally reads bytes) when the pre-signed download URL has expired, the file was deleted server-side, or the file host returns 403/404/5xx.
Common situations: Holding a RemoteFile for a long time before downloading, letting the signed URL expire; files removed by retention policy; storage provider outages; region-restricted access.
Related errors
- Failed to fetch file from URL: {e.status_code} {e.status_tex
- Failed to download file: {message} (status_code, status_text
- Response size exceeds maximum allowed size ({max_size} bytes
- Unsafe path component: {e}
- Error downloading file: {_sanitize_url_for_logging(self.s3ur
AI-assisted analysis of ComposioHQ/composio@64b1b85502 (2026-08-28).
Data as JSON: /api/errors/627cd213dc7cba13.
Report an issue: GitHub.