danny-avila/LibreChat · error
Invalid avatar URL
Error message
Invalid avatar URL
What it means
Thrown by fetchAvatarBuffer when the input string cannot be parsed by the URL constructor. This is the first validation gate in the avatar fetch pipeline: if new URL(input) throws, the input is not a valid absolute URL (it may be a relative path, a bare string, undefined coerced to string, or a malformed URL with invalid syntax). The error fires before any protocol or SSRF checks.
Source
Thrown at api/server/services/Files/images/avatar.js:34
/**
* Fetches an image URL with SSRF protection: rejects non-http(s) schemes,
* blocks resolution to private/loopback/link-local IPs at TCP connect time,
* refuses to follow redirects to prevent post-validation rebinding, and caps
* the response body so a hostile payload cannot exhaust memory before
* `sharp()` rejects it.
*
* Per-call agent construction is intentional: avatar fetches are infrequent
* (once per social login per user) and pooling adds complexity without a
* measurable benefit on this path. If this ever becomes a hot path, hoist
* the agents to module scope.
*/
async function fetchAvatarBuffer(input, fetchOptions = {}) {
let parsed;
try {
parsed = new URL(input);
} catch {
throw new Error('Invalid avatar URL');
}
if (!ALLOWED_AVATAR_PROTOCOLS.has(parsed.protocol)) {
throw new Error(`Refusing to fetch avatar over ${parsed.protocol}`);
}
const { httpAgent, httpsAgent } = createSSRFSafeAgents();
/**
* `node-fetch` v2's `timeout` is the total request budget (request initiation
* through full body receipt), not a TCP-connect-only timeout. That is the
* stronger of the two for this path — bounds total slow-loris exposure.
*/
const response = await fetch(parsed.href, {
headers: fetchOptions.headers,
agent: (urlObj) => (urlObj.protocol === 'https:' ? httpsAgent : httpAgent),
redirect: 'error',
timeout: 5000,
size: MAX_AVATAR_BYTES,
});View on GitHub (pinned to 5ff282f900)
Solutions
- Validate the avatar URL is an absolute http(s) URL before calling resizeAvatar — use a try/catch around new URL() or a regex.
- Handle null/undefined/empty picture fields from OAuth providers by falling back to a default avatar.
- If the provider returns a relative URL, prepend the provider's base domain (e.g., 'https://avatars.githubusercontent.com' for GitHub).
- Add a type check: if typeof input !== 'string', route to the Buffer or File branch instead of attempting URL parsing.
Example fix
// before
const buffer = await resizeAvatar({ userId, input: profile.picture });
// after — validate and fallback
const avatarUrl = profile.picture;
if (typeof avatarUrl === 'string' && /^https?:\/\//.test(avatarUrl)) {
const buffer = await resizeAvatar({ userId, input: avatarUrl });
} else {
const buffer = await resizeAvatar({ userId, input: DEFAULT_AVATAR_URL });
} Defensive patterns
Strategy: validation
Validate before calling
function isValidAvatarUrl(input: unknown): input is string {
if (typeof input !== 'string') return false;
try {
const parsed = new URL(input);
return parsed.protocol === 'http:' || parsed.protocol === 'https:';
} catch {
return false;
}
}
if (!isValidAvatarUrl(picture)) {
picture = DEFAULT_AVATAR_URL;
} Type guard
function isValidAvatarUrl(input: unknown): input is string {
if (typeof input !== 'string') return false;
try {
const parsed = new URL(input);
return parsed.protocol === 'http:' || parsed.protocol === 'https:';
} catch {
return false;
}
} Try / catch
try {
const buffer = await resizeAvatar({ userId, input: picture });
} catch (error) {
if (error.message === 'Invalid avatar URL') {
// fallback to default avatar
const buffer = await resizeAvatar({ userId, input: DEFAULT_AVATAR_URL });
}
throw error;
} Prevention
- Validate avatar URLs from OAuth providers before persisting — they may be null, empty, or relative.
- Fall back to a default avatar when the provider's picture URL is missing or invalid.
- Prepend the provider's base domain if the OAuth provider returns a relative avatar path.
- Type-check input before calling resizeAvatar to route Buffers/Files correctly.
When it happens
Trigger: Calling fetchAvatarBuffer(input) or resizeAvatar({ input }) where input is a string that the URL constructor rejects — e.g., '/path/to/avatar.png' (relative), '' (empty), 'not-a-url', 'ftp://...' (would pass URL parse but fail protocol check), or a value with invalid characters.
Common situations: A social login provider (Google, GitHub, Facebook) returns a relative avatar URL or an empty/null picture field. Or the user's profile picture field in the database is null/undefined and gets coerced to the string 'undefined'. Or a frontend bug sends a relative path instead of an absolute URL. Or the input is a Buffer or File but typeof input !== 'string' is true and the code path incorrectly reaches the string branch.
Related errors
- Refusing to fetch avatar over ${parsed.protocol}
- Invalid input type. Expected URL, Buffer, or File.
- Missing required field: prompt
- Missing required field: prompt
- Missing required field: finetune_id for finetuned generation
AI-assisted analysis of danny-avila/LibreChat@5ff282f900 (2026-08-12).
Data as JSON: /api/errors/0fc764291d40f88f.
Report an issue: GitHub.