SillyTavern/SillyTavern · warning

No upload data specified

Error message

No upload data specified

What it means

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.

Source

Thrown at src/endpoints/files.js:35

            return response.status(400).send('No fileName specified');
        }

        const sanitizedFilename = sanitize(fileName);
        return response.send({ fileName: sanitizedFilename });
    } catch (error) {
        console.error(error);
        return response.sendStatus(500);
    }
});

router.post('/upload', async (request, response) => {
    try {
        if (!request.body.name) {
            return response.status(400).send('No upload name specified');
        }

        if (!request.body.data) {
            return response.status(400).send('No upload data specified');
        }

        // Validate filename
        const validation = validateAssetFileName(request.body.name);
        if (validation.error)
            return response.status(400).send(validation.message);

        const pathToUpload = path.join(request.user.directories.files, request.body.name);
        writeFileSyncAtomic(pathToUpload, request.body.data, 'base64');
        const url = clientRelativePath(request.user.directories.root, pathToUpload);
        console.info(`Uploaded file: ${url} from ${request.user.profile.handle}`);
        return response.send({ path: url });
    } catch (error) {
        console.error(error);
        return response.sendStatus(500);
    }
});

View on GitHub (pinned to 8172dcd0ee)

Solutions

  1. Ensure request.body.data holds the base64-encoded file contents before submitting.
  2. On the client, read the file to a data URL and pass the base64 portion (split(',')[1]) as data.
  3. If uploading via multipart, switch to the JSON shape this route expects or use the dedicated multer endpoint.

Example fix

// before
fetch('/api/files/upload', { method:'POST', body: JSON.stringify({ name: file.name }) });
// after
const dataUrl = await readFileAsDataURL(file);
fetch('/api/files/upload', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ name: file.name, data: dataUrl.split(',')[1] }) });
Defensive patterns

Strategy: validation

Validate before calling

function readFileAsBase64(file) {
  return new Promise((resolve, reject) => {
    const r = new FileReader();
    r.onload = () => resolve(String(r.result).split(',')[1]);
    r.onerror = () => reject(r.error);
    r.readAsDataURL(file);
  });
}

Prevention

When it happens

Trigger: POST /api/files/upload with { name: 'x.txt' } but no data, or data: ''.

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

Related errors


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