{"record":{"id":"a54cec436525abd5","repo":"SillyTavern/SillyTavern","slug":"no-upload-data-specified","errorCode":null,"errorMessage":"No upload data specified","messagePattern":"No upload data specified","errorType":"http","errorClass":null,"httpStatus":400,"severity":"warning","filePath":"src/endpoints/files.js","lineNumber":35,"sourceCode":"            return response.status(400).send('No fileName specified');\n        }\n\n        const sanitizedFilename = sanitize(fileName);\n        return response.send({ fileName: sanitizedFilename });\n    } catch (error) {\n        console.error(error);\n        return response.sendStatus(500);\n    }\n});\n\nrouter.post('/upload', async (request, response) => {\n    try {\n        if (!request.body.name) {\n            return response.status(400).send('No upload name specified');\n        }\n\n        if (!request.body.data) {\n            return response.status(400).send('No upload data specified');\n        }\n\n        // Validate filename\n        const validation = validateAssetFileName(request.body.name);\n        if (validation.error)\n            return response.status(400).send(validation.message);\n\n        const pathToUpload = path.join(request.user.directories.files, request.body.name);\n        writeFileSyncAtomic(pathToUpload, request.body.data, 'base64');\n        const url = clientRelativePath(request.user.directories.root, pathToUpload);\n        console.info(`Uploaded file: ${url} from ${request.user.profile.handle}`);\n        return response.send({ path: url });\n    } catch (error) {\n        console.error(error);\n        return response.sendStatus(500);\n    }\n});\n","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/SillyTavern/SillyTavern/blob/8172dcd0ee672d3cd9a5e5f7af134f91a45cd2b8/src/endpoints/files.js#L17-L53","documentation":"Returned (HTTP 400) by POST /api/files/upload when request.body.name is present but request.body.data is falsy. The data field is written to disk as base64 via writeFileSyncAtomic(pathToUpload, request.body.data, 'base64'); an empty data field would produce an empty file, so it is rejected up front.","triggerScenarios":"POST /api/files/upload with { name: 'x.txt' } but no data, or data: ''.","commonSituations":"File reader (FileReader.readAsDataURL / arrayBuffer) failed to populate the data variable before the request was built; client sent the file in multipart form instead of the expected JSON base64 envelope.","solutions":["Ensure request.body.data holds the base64-encoded file contents before submitting.","On the client, read the file to a data URL and pass the base64 portion (split(',')[1]) as data.","If uploading via multipart, switch to the JSON shape this route expects or use the dedicated multer endpoint."],"exampleFix":"// before\nfetch('/api/files/upload', { method:'POST', body: JSON.stringify({ name: file.name }) });\n// after\nconst dataUrl = await readFileAsDataURL(file);\nfetch('/api/files/upload', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ name: file.name, data: dataUrl.split(',')[1] }) });","handlingStrategy":"validation","validationCode":"function readFileAsBase64(file) {\n  return new Promise((resolve, reject) => {\n    const r = new FileReader();\n    r.onload = () => resolve(String(r.result).split(',')[1]);\n    r.onerror = () => reject(r.error);\n    r.readAsDataURL(file);\n  });\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Read the file to a data URL and pass the base64 portion as data.","Do not submit until FileReader has finished; handle onerror.","Use the JSON base64 envelope these routes expect, not multipart."],"tags":["files","upload","validation","request-body"],"backgroundTag":null,"analyzedSha":"8172dcd0ee672d3cd9a5e5f7af134f91a45cd2b8","analyzedAt":"2026-08-13T07:48:40.832Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}