{"record":{"id":"7abd2f316e8ea5c7","repo":"SillyTavern/SillyTavern","slug":"error-no-avatar-url-in-request-body","errorCode":null,"errorMessage":"Error: no avatar_url in request body","messagePattern":"Error: no avatar_url in request body","errorType":"http","errorClass":null,"httpStatus":400,"severity":"warning","filePath":"src/endpoints/characters.js","lineNumber":1147,"sourceCode":"            // Bust cache to reload the new avatar\n            cacheBuster.bust(request, response);\n        }\n\n        return response.sendStatus(200);\n    } catch (err) {\n        console.error('An error occurred, character edit invalidated.', err);\n        return response.sendStatus(500);\n    }\n});\n\nrouter.post('/edit-avatar', validateAvatarUrlMiddleware, async function (request, response) {\n    try {\n        if (!request.file) {\n            return response.status(400).send('Error: no file uploaded');\n        }\n\n        if (!request.body || !request.body.avatar_url) {\n            return response.status(400).send('Error: no avatar_url in request body');\n        }\n\n        const uploadPath = path.join(request.file.destination, request.file.filename);\n        if (!fs.existsSync(uploadPath)) {\n            return response.status(400).send('Error: uploaded file does not exist');\n        }\n        const characterPath = path.join(request.user.directories.characters, request.body.avatar_url);\n        if (!fs.existsSync(characterPath)) {\n            return response.status(400).send('Error: character file does not exist');\n        }\n        const data = await readCharacterData(characterPath);\n        if (!data) {\n            return response.status(400).send('Error: failed to read character data');\n        }\n\n        const crop = tryParse(request.query.crop);\n        const fileName = request.body.avatar_url.replace('.png', '');\n        await writeCharacterData(uploadPath, data, fileName, request, crop);","sourceCodeStart":1129,"sourceCodeEnd":1165,"githubUrl":"https://github.com/SillyTavern/SillyTavern/blob/8172dcd0ee672d3cd9a5e5f7af134f91a45cd2b8/src/endpoints/characters.js#L1129-L1165","documentation":"POST /api/characters/edit-avatar needs request.body.avatar_url to know which existing character's embedded data should be transplanted onto the new image (src/endpoints/characters.js:1146). If the body is missing or lacks avatar_url, the handler returns 400 'Error: no avatar_url in request body'.","triggerScenarios":"Multipart form submitted with only a file and no avatar_url text field; avatar_url field left empty; body entirely empty.","commonSituations":"Frontend forgot to include the avatar_url form field alongside the file; user tried to edit an avatar for a character that was never saved.","solutions":["Append avatar_url as a multipart text field referencing an existing <name>.png character file.","Ensure the character was created first via /create before calling /edit-avatar.","Refresh the character list and use a current avatar_url value."],"exampleFix":"// before\nconst fd = new FormData();\nfd.append('avatar', file);\n// after\nconst fd = new FormData();\nfd.append('avatar', file);\nfd.append('avatar_url', 'Aiko.png');","handlingStrategy":"validation","validationCode":"const fd = new FormData();\nfd.append('avatar', file, file.name);\nif (!avatarUrl) throw new Error('avatar_url is required');\nfd.append('avatar_url', avatarUrl);","typeGuard":"const hasAvatarUrl = (s) => typeof s === 'string' && s.length > 0 && s.endsWith('.png');","tryCatchPattern":null,"preventionTips":["Always append avatar_url as a form field on avatar-edit requests.","Only offer avatar editing for already-created characters.","Refresh the character list to obtain a current avatar_url."],"tags":["express","validation","upload","client-error"],"backgroundTag":null,"analyzedSha":"8172dcd0ee672d3cd9a5e5f7af134f91a45cd2b8","analyzedAt":"2026-08-13T07:48:40.832Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}