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

  1. Inspect the server console — console.error(error) prints the syscall code and path.
  2. Close any editor/IDE/terminal that has the chat file open, and exclude the data folder from antivirus/on-demand scanning.
  3. Retry the delete after a moment if a transient lock was the cause.
  4. 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

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

Related errors


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)