{"record":{"id":"e75ba1ccca63a7cf","repo":"ComposioHQ/composio","slug":"failed-to-fetch-file-response-statustext-e75ba1","errorCode":null,"errorMessage":"Failed to fetch file: ${response.statusText}","messagePattern":"Failed to fetch file: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ts/packages/core/src/utils/fileUtils.node.ts","lineNumber":182,"sourceCode":"    };\n  } catch (error) {\n    throw new Error(`Error reading file at ${filePath}: ${error}`);\n  }\n};\n\nconst readFileContentFromURL = async (\n  path: string,\n  signal?: AbortSignal\n): Promise<{ fileName: string; content: string; mimeType: string }> => {\n  // SSRF guard: `path` is user-supplied (and can come from an LLM-produced tool\n  // argument), so it must not be allowed to reach internal/private addresses or\n  // redirect into them. See ssrfGuard.node.ts.\n  const response = await ssrfSafeFetch(path, { signal });\n  if (!response.ok) {\n    // The error path never reads the body, so release it explicitly (mirrors\n    // `readResponseBodyWithLimit`) instead of leaving it to the garbage collector.\n    await response.body?.cancel().catch(() => undefined);\n    throw new Error(`Failed to fetch file: ${response.statusText}`);\n  }\n  const content = await readResponseBodyWithLimit(response);\n  const mimeType = response.headers.get('content-type') || 'application/octet-stream';\n\n  // Extract clean filename from URL, removing query parameters\n  const url = new URL(path);\n  const pathname = url.pathname;\n  let fileName = platform.basename(pathname);\n\n  // If no filename from URL, generate one with appropriate extension\n  if (!fileName || fileName === '/') {\n    // Try to get extension from mimeType\n    const extension = getExtensionFromMimeType(mimeType);\n    fileName = generateTimestampedFilename(extension);\n  } else {\n    // If filename has no extension, try to add one from mimeType\n    const hasExtension = fileName.includes('.');\n    if (!hasExtension) {","sourceCodeStart":164,"sourceCodeEnd":200,"githubUrl":"https://github.com/ComposioHQ/composio/blob/64b1b85502b1beeb2379e6c9e8bf1104504fa637/ts/packages/core/src/utils/fileUtils.node.ts#L164-L200","documentation":"Thrown when the SSRF-guarded fetch of a remote file URL returns a non-OK HTTP status. The response body is explicitly cancelled before throwing, and only statusText is surfaced.","triggerScenarios":"Passing an http(s) URL to readFile/getFileDataAfterUploadingToS3 where the server responds 4xx/5xx: expired signed S3 links (403), removed files (404), or rate limiting.","commonSituations":"Expired pre-signed URLs, mistyped domains, servers requiring auth headers the SDK does not send, or transient 5xx from object storage.","solutions":["Retry with backoff for transient 5xx/429 statuses.","Verify the URL opens in curl/browser; re-generate expired signed links.","Ensure the URL is publicly reachable without cookies/auth if the endpoint needs them."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"const ok = await fetch(url, { method: 'HEAD' }).then(r => r.ok).catch(() => false);","typeGuard":null,"tryCatchPattern":"try { await readFile(url); } catch (e) {\n  if ((e as Error).message.startsWith('Failed to fetch file:')) { /* retry or refresh URL */ }\n}","preventionTips":["Re-generate short-lived signed URLs right before upload.","Add retry with backoff for 429/5xx."],"tags":["network","http","fetch","file-upload","typescript"],"backgroundTag":"http-request-failed","analyzedSha":"64b1b85502b1beeb2379e6c9e8bf1104504fa637","analyzedAt":"2026-08-28T15:39:33.623Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}