{"record":{"id":"748ebe1432c82b93","repo":"danny-avila/LibreChat","slug":"failed-to-fetch-url-response-status-response-748ebe","errorCode":null,"errorMessage":"Failed to fetch URL: ${response.status} ${response.statusText}","messagePattern":"Failed to fetch URL: (.+?) (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"api/server/services/Files/Firebase/crud.js","lineNumber":73,"sourceCode":" *\n * @returns {Promise<{ bytes: number, type: string, dimensions: Record<string, number>} | null>}\n *          A promise that resolves to the file metadata if the file is successfully saved, or null if there is an error.\n */\nasync function saveURLToFirebase({ userId, URL, fileName, basePath = 'images' }) {\n  const storage = getFirebaseStorage();\n  if (!storage) {\n    logger.error('Firebase is not initialized. Cannot save file to Firebase Storage.');\n    return null;\n  }\n\n  const storageRef = ref(storage, `${basePath}/${userId.toString()}/${fileName}`);\n  const maxBytes = getRemoteFileFetchMaxBytes();\n  const response = await fetch(assertRemoteFileURL(URL), {\n    timeout: getRemoteFileFetchTimeoutMs(),\n    size: maxBytes,\n  });\n  if (!response.ok) {\n    throw new Error(`Failed to fetch URL: ${response.status} ${response.statusText}`);\n  }\n  assertRemoteFileContentLength(response.headers, maxBytes);\n  const buffer = await response.buffer();\n  if (buffer.length > maxBytes) {\n    throw new Error(`Remote file response too large: ${buffer.length} bytes`);\n  }\n\n  try {\n    await uploadBytes(storageRef, buffer);\n    return await getBufferMetadata(buffer);\n  } catch (error) {\n    logger.error('Error uploading file to Firebase Storage:', error.message);\n    return null;\n  }\n}\n\n/**\n * Retrieves the download URL for a specified file from Firebase Storage. This function initializes the","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/danny-avila/LibreChat/blob/5ff282f9006c436e561de1afd39a481bea1ef0d8/api/server/services/Files/Firebase/crud.js#L55-L91","documentation":"Thrown by saveURLToFirebase (Firebase/crud.js) when the remote-file fetch returns a non-2xx status, before attempting uploadBytes to the Firebase storage ref. Identical contract to the Azure variant (error 81) — both share assertRemoteFileURL, timeout, and size caps.","triggerScenarios":"A remote URL passed to the Firebase save path returns non-2xx (404/403/500) from the origin, or the origin redirects into an error.","commonSituations":"Expired signed URL; origin requires auth; URL typo or dead link; geo-blocked origin; origin server down.","solutions":["Verify the URL is publicly reachable (curl -I returns 2xx).","If the origin needs auth, fetch via a proxy that injects headers.","Distinguish 403/404 in the caller to give the user an accurate message.","Confirm the URL scheme is http/https (assertRemoteFileURL enforces this)."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// HEAD-check the URL before downloading\nconst head = await fetch(assertRemoteFileURL(URL), { method: 'HEAD', timeout: getRemoteFileFetchTimeoutMs() });\nif (!head.ok) {\n  throw new Error(`Preflight failed: ${head.status} ${head.statusText}`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await saveURLToFirebase({ userId, URL, fileName, basePath });\n} catch (err) {\n  const m = /^Failed to fetch URL: (\\d{3})/.exec(err.message);\n  if (m) {\n    const status = Number(m[1]);\n    return res.status(status === 404 || status === 403 ? status : 502)\n      .json({ message: `Remote URL returned ${status}` });\n  }\n  throw err;\n}","preventionTips":["Preflight URLs with HEAD and honor Content-Length/Status.","Surface the remote status code to the user rather than a generic 500.","Keep assertRemoteFileURL on the path to block non-http(s) schemes."],"tags":["network","firebase","remote-fetch","storage"],"backgroundTag":null,"analyzedSha":"5ff282f9006c436e561de1afd39a481bea1ef0d8","analyzedAt":"2026-08-12T21:38:08.145Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}