{"record":{"id":"c2f8ee14f72ab6b9","repo":"danny-avila/LibreChat","slug":"avatar-response-too-large-contentlength-bytes","errorCode":null,"errorMessage":"Avatar response too large: ${contentLength} bytes","messagePattern":"Avatar response too large: (.+?) bytes","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"api/server/services/Files/images/avatar.js","lineNumber":60,"sourceCode":"   * `node-fetch` v2's `timeout` is the total request budget (request initiation\n   * through full body receipt), not a TCP-connect-only timeout. That is the\n   * stronger of the two for this path — bounds total slow-loris exposure.\n   */\n  const response = await fetch(parsed.href, {\n    headers: fetchOptions.headers,\n    agent: (urlObj) => (urlObj.protocol === 'https:' ? httpsAgent : httpAgent),\n    redirect: 'error',\n    timeout: 5000,\n    size: MAX_AVATAR_BYTES,\n  });\n\n  if (!response.ok) {\n    throw new Error(`Failed to fetch image from URL. Status: ${response.status}`);\n  }\n\n  const contentLength = parseInt(response.headers.get('content-length') ?? '0', 10);\n  if (contentLength > MAX_AVATAR_BYTES) {\n    throw new Error(`Avatar response too large: ${contentLength} bytes`);\n  }\n\n  /**\n   * Re-check after read in case the server lied about Content-Length or\n   * omitted it. `node-fetch` v2 honors the `size` option above and throws on\n   * overflow, but Defense-in-depth: assert on the actual buffer length.\n   */\n  const buffer = await response.buffer();\n  if (buffer.length > MAX_AVATAR_BYTES) {\n    throw new Error(`Avatar response too large: ${buffer.length} bytes`);\n  }\n  return buffer;\n}\n\n/**\n * Uploads an avatar image for a user. This function can handle various types of input (URL, Buffer, or File object),\n * processes the image to a square format, converts it to target format, and returns the resized buffer.\n *","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/danny-avila/LibreChat/blob/5ff282f9006c436e561de1afd39a481bea1ef0d8/api/server/services/Files/images/avatar.js#L42-L78","documentation":"Thrown when the response's `Content-Length` header, parsed as an integer, exceeds MAX_AVATAR_BYTES (10 MB). This is a pre-read guard: it inspects the declared size before buffering the body so a hostile or buggy origin cannot exhaust memory. A missing header (parsed as 0) does not trip this; the post-read guard (error 123) catches that case.","triggerScenarios":"Avatar URL points to a high-resolution uncropped source image, a video mislabeled as an image, or a malicious payload whose declared Content-Length is over 10 MB. Also a CDN returning the wrong (huge) Content-Length for a small body.","commonSituations":"Users pasting a link to a raw camera RAW or print-resolution photo as their avatar; a CDN misconfiguration serving a multi-GB master instead of a thumbnail; an attacker probing the size cap with synthetic payloads.","solutions":["Point the avatar URL at a properly sized thumbnail (a few hundred KB), not the original upload.","If you control the origin, configure it to serve a < 10 MB derivative for the avatar route.","Pre-check the URL's HEAD response Content-Length in the caller and reject early with a user-facing message.","Increase MAX_AVATAR_BYTES only if you genuinely need larger avatars (not recommended — sharp will resize down anyway)."],"exampleFix":"// before\nawait fetchAvatarBuffer('https://cdn.example.com/user/original-raw.dng');\n\n// after: caller pre-check\nconst head = await fetch(url, { method: 'HEAD' });\nconst len = parseInt(head.headers.get('content-length') ?? '0', 10);\nif (len > 10 * 1024 * 1024) throw new Error('Avatar too large; please use a smaller image.');\nawait fetchAvatarBuffer(url);","handlingStrategy":"validation","validationCode":"const MAX = 10 * 1024 * 1024;\nasync function assertAvatarSize(url) {\n  const head = await fetch(url, { method: 'HEAD', timeout: 5000 });\n  const len = parseInt(head.headers.get('content-length') ?? '0', 10);\n  if (len > MAX) throw new Error(`Avatar too large (${len} bytes) before download`);\n}","typeGuard":null,"tryCatchPattern":"try { await fetchAvatarBuffer(url); }\ncatch (e) {\n  if (/Avatar response too large/.test(e.message)) return res.status(413).json({ error: 'Avatar image too large; please use a smaller one.' });\n  throw e;\n}","preventionTips":["Serve avatar derivatives (thumbnails), not originals, from your CDN.","Reject oversized avatars client-side after picking the file.","Do not raise the 10 MB cap to mask an untrustworthy origin."],"tags":["avatar","size-limit","content-length","memory"],"backgroundTag":null,"analyzedSha":"5ff282f9006c436e561de1afd39a481bea1ef0d8","analyzedAt":"2026-08-12T21:38:08.145Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}