SillyTavern/SillyTavern · warning

User not found

Error message

User not found

What it means

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.

Source

Thrown at src/endpoints/users-private.js:81

        }

        if (request.body.handle !== request.user.profile.handle && !request.user.profile.admin) {
            console.error('Change avatar failed: Unauthorized');
            return response.status(403).json({ error: 'Unauthorized' });
        }

        // Avatar is not a data URL or not an empty string
        if (!request.body.avatar.startsWith('data:image/') && request.body.avatar !== '') {
            console.warn('Change avatar failed: Invalid data URL');
            return response.status(400).json({ error: 'Invalid data URL' });
        }

        /** @type {import('../users.js').User} */
        const user = await storage.getItem(toKey(request.body.handle));

        if (!user) {
            console.error('Change avatar failed: User not found');
            return response.status(404).json({ error: 'User not found' });
        }

        await storage.setItem(toAvatarKey(request.body.handle), request.body.avatar);

        return response.sendStatus(204);
    } catch (error) {
        console.error(error);
        return response.sendStatus(500);
    }
});

router.post('/change-password', async (request, response) => {
    try {
        if (!request.body.handle) {
            console.warn('Change password failed: Missing required fields');
            return response.status(400).json({ error: 'Missing required fields' });
        }

View on GitHub (pinned to 8172dcd0ee)

Solutions

  1. Verify the handle exists by calling GET /api/users/get (admin) and matching exactly.
  2. Use the exact handle string returned by /me or /get, preserving case.
  3. If the user was deleted, refresh the user list in the UI before allowing avatar changes.

Example fix

// before
body: JSON.stringify({ handle: 'SomeUser', avatar: dataUrl })
// after — fetch the canonical handle first
const users = await fetch('/api/users/get').then(r => r.json());
const match = users.find(u => u.name === 'SomeUser');
if (!match) throw new Error('user not found');
body: JSON.stringify({ handle: match.handle, avatar: dataUrl })
Defensive patterns

Strategy: validation

Validate before calling

async function ensureUserExists(handle) {
  const users = await fetch('/api/users/get', { method:'POST' }).then(r => r.json());
  if (!users.some(u => u.handle === handle)) throw new Error('handle not found: ' + handle);
}

Try / catch

const res = await fetch('/api/users/change-avatar', { ... });
if (res.status === 404) { showUserNotFoundError(); return; }

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of SillyTavern/SillyTavern@8172dcd0ee (2026-08-13). Data as JSON: /api/errors/c2b1fd28cefea092. Report an issue: GitHub.