{"record":{"id":"c2b1fd28cefea092","repo":"SillyTavern/SillyTavern","slug":"user-not-found-c2b1fd","errorCode":null,"errorMessage":"User not found","messagePattern":"User not found","errorType":"http","errorClass":null,"httpStatus":404,"severity":"warning","filePath":"src/endpoints/users-private.js","lineNumber":81,"sourceCode":"        }\n\n        if (request.body.handle !== request.user.profile.handle && !request.user.profile.admin) {\n            console.error('Change avatar failed: Unauthorized');\n            return response.status(403).json({ error: 'Unauthorized' });\n        }\n\n        // Avatar is not a data URL or not an empty string\n        if (!request.body.avatar.startsWith('data:image/') && request.body.avatar !== '') {\n            console.warn('Change avatar failed: Invalid data URL');\n            return response.status(400).json({ error: 'Invalid data URL' });\n        }\n\n        /** @type {import('../users.js').User} */\n        const user = await storage.getItem(toKey(request.body.handle));\n\n        if (!user) {\n            console.error('Change avatar failed: User not found');\n            return response.status(404).json({ error: 'User not found' });\n        }\n\n        await storage.setItem(toAvatarKey(request.body.handle), request.body.avatar);\n\n        return response.sendStatus(204);\n    } catch (error) {\n        console.error(error);\n        return response.sendStatus(500);\n    }\n});\n\nrouter.post('/change-password', async (request, response) => {\n    try {\n        if (!request.body.handle) {\n            console.warn('Change password failed: Missing required fields');\n            return response.status(400).json({ error: 'Missing required fields' });\n        }\n","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/SillyTavern/SillyTavern/blob/8172dcd0ee672d3cd9a5e5f7af134f91a45cd2b8/src/endpoints/users-private.js#L63-L99","documentation":"Returned as HTTP 404 by POST /api/users/change-avatar when storage.getItem(toKey(handle)) returns a falsy value (users-private.js:79). This means no user record exists under that handle in node-persist. The handler passed the handle/authorization/URL checks but the target account is not present in storage.","triggerScenarios":"The handle was deleted, never created, or was passed with different casing/whitespace than how it is stored (the key uses toKey(handle) without normalization on this route, unlike /create which slugifies).","commonSituations":"Client cached a stale handle after the user was deleted; a typo in the handle; case mismatch (storage keys are case-sensitive); the user exists only in the default fallback and not as a stored record.","solutions":["Verify the handle exists by calling GET /api/users/get (admin) and matching exactly.","Use the exact handle string returned by /me or /get, preserving case.","If the user was deleted, refresh the user list in the UI before allowing avatar changes."],"exampleFix":"// before\nbody: JSON.stringify({ handle: 'SomeUser', avatar: dataUrl })\n// after — fetch the canonical handle first\nconst users = await fetch('/api/users/get').then(r => r.json());\nconst match = users.find(u => u.name === 'SomeUser');\nif (!match) throw new Error('user not found');\nbody: JSON.stringify({ handle: match.handle, avatar: dataUrl })","handlingStrategy":"validation","validationCode":"async function ensureUserExists(handle) {\n  const users = await fetch('/api/users/get', { method:'POST' }).then(r => r.json());\n  if (!users.some(u => u.handle === handle)) throw new Error('handle not found: ' + handle);\n}","typeGuard":null,"tryCatchPattern":"const res = await fetch('/api/users/change-avatar', { ... });\nif (res.status === 404) { showUserNotFoundError(); return; }","preventionTips":["Use the exact handle string returned by /me or /get (case-sensitive).","Refresh the user list after create/delete operations.","Do not confuse display 'name' with 'handle'."],"tags":["http-404","user-private","avatar","node-persist","not-found"],"backgroundTag":null,"analyzedSha":"8172dcd0ee672d3cd9a5e5f7af134f91a45cd2b8","analyzedAt":"2026-08-13T07:48:40.832Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}