{"record":{"id":"62c938a8a19bdf47","repo":"ComposioHQ/composio","slug":"failed-to-fetch-file-response-statustext","errorCode":null,"errorMessage":"Failed to fetch file: ${response.statusText}","messagePattern":"Failed to fetch file: (.+?)","errorType":"exception","errorClass":"ToolFileUploadError","httpStatus":null,"severity":"error","filePath":"ts/packages/cli/src/services/tool-file-uploads.ts","lineNumber":148,"sourceCode":"        isSchemaRecord(item) ? findFileUploadablePaths(item, basePath) : []\n      )\n    : isSchemaRecord(schema.items)\n      ? findFileUploadablePaths(schema.items, basePath)\n      : [];\n\n  const seen = new Set<string>();\n  return [...directPropertyPaths, ...variantPaths, ...itemPaths].filter(pathParts => {\n    const key = pathParts.join('.');\n    if (seen.has(key)) return false;\n    seen.add(key);\n    return true;\n  });\n};\n\nconst readFileFromUrl = async (path: Path.Path, url: string) => {\n  const response = await fetch(url);\n  if (!response.ok) {\n    throw new ToolFileUploadError({\n      message: `Failed to fetch file: ${response.statusText}`,\n      reason: 'source-fetch',\n      status: response.status,\n    });\n  }\n\n  const bytes = new Uint8Array(await response.arrayBuffer());\n  const parsedUrl = new URL(url);\n  const fileName = path.basename(parsedUrl.pathname) || `file-${Date.now()}`;\n\n  return {\n    bytes,\n    fileName,\n    mimeType: response.headers.get('content-type') || 'application/octet-stream',\n  };\n};\n\n// Runs the FileSystem read to completion for the plain-async pipeline above.","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/ComposioHQ/composio/blob/64b1b85502b1beeb2379e6c9e8bf1104504fa637/ts/packages/cli/src/services/tool-file-uploads.ts#L130-L166","documentation":"When an upload source is a URL, readFileFromUrl fetches it and throws ToolFileUploadError with reason 'source-fetch' if the HTTP response is not ok, embedding the statusText (e.g. 'Not Found', 'Forbidden').","triggerScenarios":"Passing a URL as the file source where the server returns 4xx/5xx: expired presigned link, private file requiring auth, 404 from a typo'd path, or a server-side 5xx.","commonSituations":"Presigned S3 URLs past their expiry, links requiring headers/cookies the plain fetch does not send, typo'd or deleted files, or transient upstream outages.","solutions":["Verify the URL opens in a browser/curl with the same anonymity (no auth)","If the link is presigned and expired, generate a fresh one","Add retry with backoff for transient 5xx before starting the upload flow","Download manually and pass the local file path instead of the URL"],"exampleFix":"# before\ncomposio ... --file https://example.com/expired-presigned-url\n# after\ncurl -I <url>   # confirm 200\ncomposio ... --file ./downloaded-file.pdf","handlingStrategy":"retry","validationCode":"const ok = await fetch(url, { method: 'HEAD' }).then(r => r.ok).catch(() => false);\nif (!ok) throw new Error('source URL unreachable');","typeGuard":null,"tryCatchPattern":"catch (e) { if (e instanceof ToolFileUploadError && e.reason === 'source-fetch') { await backoff(); retryUpload(); } throw e; }","preventionTips":["Verify URLs with curl before batch runs","Refresh presigned links at use time","Fall back to a local file path when the URL is flaky"],"tags":["file-upload","fetch","http","url"],"backgroundTag":"http-request-failed","analyzedSha":"64b1b85502b1beeb2379e6c9e8bf1104504fa637","analyzedAt":"2026-08-28T15:39:33.623Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}