{"record":{"id":"3adffb0d0b623b74","repo":"RocketChat/Rocket.Chat","slug":"error-avatar-url-handling","errorCode":"error-avatar-url-handling","errorMessage":"Error while handling avatar setting from a URL (${dataURI}) for ${user.username}","messagePattern":"Error while handling avatar setting from a URL \\((.+?)\\) for (.+?)","errorType":"exception","errorClass":"Meteor.Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/lib/users/setUserAvatar.ts","lineNumber":131,"sourceCode":"\t\t\t\t\tmsg: 'Not a valid response from the avatar url',\n\t\t\t\t\turl: dataURI,\n\t\t\t\t\terr: e,\n\t\t\t\t});\n\t\t\t\tthrow new Meteor.Error('error-avatar-invalid-url', `Invalid avatar URL: ${dataURI}`, {\n\t\t\t\t\tfunction: 'setUserAvatar',\n\t\t\t\t\turl: dataURI,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (response.status !== 200) {\n\t\t\t\tif (response.status !== 404) {\n\t\t\t\t\tSystemLogger.info({\n\t\t\t\t\t\tmsg: 'Error while handling the setting of the avatar from a url',\n\t\t\t\t\t\turl: dataURI,\n\t\t\t\t\t\tusername: user.username,\n\t\t\t\t\t\tstatus: response.status,\n\t\t\t\t\t});\n\t\t\t\t\tthrow new Meteor.Error(\n\t\t\t\t\t\t'error-avatar-url-handling',\n\t\t\t\t\t\t`Error while handling avatar setting from a URL (${dataURI}) for ${user.username}`,\n\t\t\t\t\t\t{ function: 'RocketChat.setUserAvatar', url: dataURI, username: user.username },\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tSystemLogger.info({\n\t\t\t\t\tmsg: 'Not a valid response from the avatar url',\n\t\t\t\t\tstatus: response.status,\n\t\t\t\t\turl: dataURI,\n\t\t\t\t});\n\t\t\t\tthrow new Meteor.Error('error-avatar-invalid-url', `Invalid avatar URL: ${dataURI}`, {\n\t\t\t\t\tfunction: 'setUserAvatar',\n\t\t\t\t\turl: dataURI,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst contentType = response.headers.get('content-type');","sourceCodeStart":113,"sourceCodeEnd":149,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/2a7de457074cbb4d4373fbd9a4e5bea292c9c764/apps/meteor/server/lib/users/setUserAvatar.ts#L113-L149","documentation":"The avatar URL was fetched successfully but responded with an HTTP status that is neither 200 nor 404 (for example 500, 403, 429, 502). The transport worked; the remote endpoint itself errored or refused the request. The url and username are included in the error details, and the status is recorded in SystemLogger.","triggerScenarios":"Image host returning 5xx during an outage; hotlink-protected or auth-required URLs answering 403; rate-limited CDN answering 429; cloud storage returning 400 for malformed or expired signed URLs.","commonSituations":"Linking avatars from an image service that requires signed/authenticated URLs; temporary CDN/provider outage; pre-signed S3/GCS links that expired between generation and avatar submission.","solutions":["Reproduce from the server with curl -I <url> and inspect the actual status code.","Use direct, unauthenticated image links or generate fresh signed URLs at submission time.","Host the image somewhere stable, or upload the bytes directly (data-URI or rest service) instead of by URL.","Retry later if the status indicates a transient 5xx/429."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Pre-flight from the server: only submit URLs that answer 200\nconst checkUrl = async (url: string): Promise<boolean> => {\n  try {\n    const res = await fetch(url, { method: 'HEAD' });\n    return res.status === 200;\n  } catch {\n    return false;\n  }\n};\nif (!(await checkUrl(avatarUrl))) {\n  throw new Error('Avatar URL did not answer 200; fix or re-sign the link');\n}","typeGuard":null,"tryCatchPattern":"const withRetry = async <T>(fn: () => Promise<T>, attempts = 3, delayMs = 1000): Promise<T> => {\n  for (let i = 0; i < attempts; i++) {\n    try {\n      return await fn();\n    } catch (e: any) {\n      if (i === attempts - 1 || e.error !== 'error-avatar-url-handling') throw e;\n      await new Promise((r) => setTimeout(r, delayMs * 2 ** i));\n    }\n  }\n  throw new Error('unreachable');\n};\n\nawait withRetry(() => setUserAvatar(user, url, undefined, 'url'));","preventionTips":["Avoid hotlink-protected or auth-gated URLs for avatars; host stable, unauthenticated image links.","Regenerate pre-signed storage URLs at submission time rather than storing long-lived ones.","Treat persistent non-200/non-404 statuses as permanent and fall back to initials or a direct upload."],"tags":["avatar","http","url","remote-error"],"backgroundTag":"http-error-response","analyzedSha":"2a7de457074cbb4d4373fbd9a4e5bea292c9c764","analyzedAt":"2026-08-18T15:26:39.429Z","contentChangedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}