toeverything/AFFiNE · info · UserAvatarNotFound
user_avatar_not_found
user_avatar_not_found
Error message
User avatar not found.
What it means
Thrown by UserAvatarController.getAvatar() after storage.get(id) returns an empty body. The provider check passed (fs/assetpack) but no blob exists for the given id, so there is nothing to stream.
Source
Thrown at packages/backend/server/src/core/user/controller.ts:29
@Public()
@Controller('/api/avatars')
export class UserAvatarController {
constructor(private readonly storage: AvatarStorage) {}
@Get('/:id')
async getAvatar(@Res() res: Response, @Param('id') id: string) {
const provider = this.storage.config.storage.provider;
if (!['assetpack', 'fs'].includes(provider)) {
throw new ActionForbidden(
'Only available when avatar storage provider is fs or assetpack.'
);
}
const { body, metadata } = await this.storage.get(id);
if (!body) {
throw new UserAvatarNotFound();
}
// metadata should always exists if body is not null
if (metadata) {
res.setHeader('content-type', metadata.contentType);
res.setHeader('last-modified', metadata.lastModified.toISOString());
res.setHeader('content-length', metadata.contentLength);
}
applyAttachHeaders(res, {
contentType: metadata?.contentType,
filename: `${id}`,
});
body.pipe(res);
}
}
View on GitHub (pinned to 26c515e050)
Solutions
- Default to a generated/fallback avatar on the client when this 404 is returned.
- Clear cached avatar URLs when the user removes their avatar.
- Verify the id being requested matches the storage key format `${userId}-avatar-${timestamp}`.
Example fix
// before
<img src={`/api/avatars/${id}`} />
// after — handle 404 with fallback
<img src={avatarUrl} onError={(e) => { e.currentTarget.src = fallbackAvatar(id); }} /> Defensive patterns
Strategy: fallback
Validate before calling
const exists = await checkAvatarExists(id); if (!exists) return fallbackAvatar(id);
Type guard
function hasAvatar(user?: { avatarUrl?: string | null }): boolean {
return Boolean(user?.avatarUrl);
} Try / catch
try {
await fetch('/api/avatars/' + id);
} catch (e) {
if (e?.code === 'user_avatar_not_found') { setFallbackAvatar(); return; }
throw e;
} Prevention
- Render avatars from user.avatarUrl and fall back to a generated avatar on error.
- Clear cached avatar URLs when removeAvatar succeeds.
- Treat the 404 as expected for users who never set an avatar.
When it happens
Trigger: GET /api/avatars/:id where the storage backend has no object for that id — avatar was never uploaded, was deleted, or the id is wrong.
Common situations: User never set an avatar but the client still requests one; avatar was removed (removeAvatar mutation) and the client cached the old URL; id mismatch after a storage migration.
Related errors
AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12).
Data as JSON: /api/errors/e36a80857c121da5.
Report an issue: GitHub.