OtterMind/Chat2DB · error · BusinessException

database.delete.systemDatabaseForbidden

database.delete.systemDatabaseForbidden

Error message

database.delete.systemDatabaseForbidden

What it means

Thrown by rejectSystemDatabase when the active dbType is MySQL and the target database name matches one of the protected system databases: information_schema, mysql, performance_schema, sys (case-insensitive). This is a hard safety guard preventing destructive DROP on MySQL's internal catalogs. It fires in both prepare and execute phases.

Source

Thrown at chat2db-community-server/chat2db-community-domain/chat2db-community-domain-core/src/main/java/ai/chat2db/community/domain/core/impl/db/DbDatabaseObjectDeleteServiceImpl.java:187

    private String requireName(String name, String fieldName) {
        String trimmed = StringUtils.trimToNull(name);
        if (trimmed == null) {
            throw new BusinessException("database.delete." + fieldName + "Required");
        }
        return trimmed;
    }

    private void assertDatabaseDeletionSupported(String dbType) {
        DBConfig dbConfig = Chat2DBContext.getDBConfig(dbType);
        if (dbConfig == null || !dbConfig.isSupportDatabase()) {
            throw new BusinessException("database.delete.notSupportDatabase");
        }
    }

    private void rejectSystemDatabase(String dbType, String databaseName) {
        if (DatabaseTypeEnum.MYSQL.name().equalsIgnoreCase(dbType)
                && MYSQL_SYSTEM_DATABASES.contains(StringUtils.lowerCase(databaseName))) {
            throw new BusinessException("database.delete.systemDatabaseForbidden");
        }
        if (DatabaseTypeEnum.POSTGRESQL.name().equalsIgnoreCase(dbType)
                && POSTGRESQL_SYSTEM_DATABASES.contains(StringUtils.lowerCase(databaseName))) {
            throw new BusinessException("database.delete.systemDatabaseForbidden");
        }
    }

    private Connection requireConnection() {
        Connection connection = Chat2DBContext.getConnection();
        if (connection == null) {
            throw new BusinessException("database.delete.connectionFailed");
        }
        return connection;
    }

    private void assertDatabaseExists(Connection connection, String dbType, String databaseName) {
        List<Database> databases = Chat2DBContext.getDbMetaData(dbType).databases(connection);
        boolean exists = databases != null && databases.stream()

View on GitHub (pinned to 5ee1e990e7)

Solutions

  1. Do not attempt to delete MySQL system databases; exclude information_schema, mysql, performance_schema, and sys from any batch operation.
  2. Filter the database list in the UI before offering the delete action so system databases are not selectable.
  3. If a custom database legitimately shares a system name, rename it before deleting.

Example fix

// before
prepareDatabaseDelete(req); // databaseName='mysql'

// after
Set<String> blocked = Set.of("information_schema","mysql","performance_schema","sys");
if (blocked.contains(StringUtils.lowerCase(req.getDatabaseName()))) {
    return; // skip system databases
}
prepareDatabaseDelete(req);
Defensive patterns

Strategy: validation

Validate before calling

Set<String> mysqlBlocked = Set.of("information_schema","mysql","performance_schema","sys");
if ("MYSQL".equalsIgnoreCase(dbType) && mysqlBlocked.contains(StringUtils.lowerCase(databaseName))) {
    // skip / disable delete
}

Prevention

When it happens

Trigger: Calling prepareDatabaseDelete or executeDatabaseDelete with databaseName equal to (case-insensitive) 'information_schema', 'mysql', 'performance_schema', or 'sys' while the connection's dbType is MySQL.

Common situations: User types a system database name in the delete-confirmation dialog; a scripted bulk-cleanup that iterates all database names without filtering system catalogs; UI listing databases and letting the user pick 'mysql'.

Understand the failure class

Related errors


AI-assisted analysis of OtterMind/Chat2DB@5ee1e990e7 (2026-08-14). Data as JSON: /api/errors/0292f3b59e82f7a7. Report an issue: GitHub.