SillyTavern/SillyTavern · error
Internal Server Error
Error message
Internal Server Error
What it means
Returned by POST /api/chats/delete as a bare 500 from the catch block when an unexpected exception is thrown inside the handler. tryDeleteFile itself only returns false (not throw), so the exception comes from elsewhere: String(request.body.chatfile) on a non-coercible body, path.join on invalid input, or fs.unlinkSync raising EBUSY/EACCES/EPERM despite the existsSync check (a TOCTOU race where the file is locked or removed between the check and unlink).
Solutions
- Inspect the server console — console.error(error) prints the syscall code and path.
- Close any editor/IDE/terminal that has the chat file open, and exclude the data folder from antivirus/on-demand scanning.
- Retry the delete after a moment if a transient lock was the cause.
- Verify filesystem permissions on the chats directory.
Example fix
// before: existsSync then unlinkSync can race on Windows
if (fs.existsSync(filePath)) { fs.unlinkSync(filePath); return true; }
// after: tolerate a vanished-or-locked file
try { fs.unlinkSync(filePath); return true; }
catch (e) { if (e.code === 'ENOENT') return false; throw e; } Defensive patterns
Strategy: retry
Validate before calling
// Server-side: replace existsSync+unlinkSync with a race-tolerant delete in tryDeleteFile // (validation happens by checking error.code === 'ENOENT' in the catch)
Try / catch
async function deleteChat(path) {
for (let attempt = 0; attempt < 3; attempt++) {
try { await fsPromises.unlink(path); return true; }
catch (e) {
if (e.code === 'ENOENT') return false; // already gone
if (e.code === 'EBUSY' || e.code === 'EPERM') { await sleep(200); continue; }
throw e;
}
}
throw new Error('file locked');
} Prevention
- Exclude the data folder from antivirus/cloud-sync real-time scanning on Windows.
- Close editors holding chat files before deleting.
- Retry once on EBUSY/EPERM before surfacing the 500.
When it happens
Trigger: On Windows, another process (editor, antivirus, indexer) holds the jsonl open so unlinkSync throws EBUSY; permissions changed between existsSync and unlinkSync; request.body.chatfile is null/non-string in a way that breaks path operations.
Common situations: Antivirus or cloud-sync (OneDrive) locking chat files on Windows; a network-mounted data volume with intermittent permission errors.
Understand the failure class
- HTTP status errors: handling 4xx and 5xx responses — how to handle 4xx and 5xx responses properly.
Related errors
- Could not read JSONL file to export. Source chat file
- An error has occurred, see the console logs for more…
- Internal Server Error
- Internal Server Error
- World info file doesn't exist.
AI-assisted analysis of SillyTavern/SillyTavern@06bde939fb (2026-08-13).
Data as JSON: /api/errors/fe632eec08634c1a.
Report an issue: GitHub.
Appendix: source
Thrown at src/endpoints/chats.js:675
request.body.chatfile += '.jsonl';
}
const dirName = String(request.body.avatar_url).replace('.png', '');
const chatFileName = String(request.body.chatfile);
const chatFilePath = path.join(request.user.directories.chats, dirName, sanitize(chatFileName));
if (!isPathUnderParent(request.user.directories.chats, chatFilePath)) {
return response.sendStatus(400);
}
//Return success if the file was deleted.
if (tryDeleteFile(chatFilePath)) {
return response.send({ ok: true });
} else {
console.error('The chat file was not deleted.');
return response.sendStatus(400);
}
} catch (error) {
console.error(error);
return response.sendStatus(500);
}
});
router.post('/export', validateAvatarUrlMiddleware, async function (request, response) {
if (!request.body.file || (!request.body.avatar_url && request.body.is_group === false)) {
return response.sendStatus(400);
}
const pathToFolder = request.body.is_group
? request.user.directories.groupChats
: path.join(request.user.directories.chats, String(request.body.avatar_url).replace('.png', ''));
const filename = path.join(pathToFolder, sanitize(request.body.file));
if (!request.body.is_group && !isPathUnderParent(request.user.directories.chats, filename)) {
return response.sendStatus(400);
}
let exportfilename = request.body.exportfilename;
if (!fs.existsSync(filename)) {
const errorMessage = {
message: `Could not find JSONL file to export. Source chat file: ${filename}.`,View on GitHub (pinned to 06bde939fb)