OtterMind/Chat2DB · error · IllegalArgumentException

Unsafe {what} name: {name}

Error message

Unsafe {what} name: {name}

What it means

Thrown by SqliteSqlGuards.requireSafeName when a name destined for a non-escapable keyword position (collation/charset) is null or does not fully match the allow-list regex [A-Za-z0-9_]+. Because these names cannot be quoted/escaped, anything outside ASCII alphanumerics and underscore is rejected to prevent DDL injection (fail closed).

Source

Thrown at chat2db-community-server/chat2db-community-plugins/chat2db-community-sqlite/src/main/java/ai/chat2db/plugin/sqlite/SqliteSqlGuards.java:53

            "AUTOINCREMENT", "CHECK", "COLLATE", "CONSTRAINT", "DEFAULT", "GENERATED",
            "NOT", "NULL", "PRIMARY", "REFERENCES", "UNIQUE");
    private static final Set<String> STATEMENT_KEYWORDS = Set.of(
            "ALTER", "ATTACH", "CREATE", "DELETE", "DETACH", "DROP", "INSERT", "PRAGMA",
            "REINDEX", "REPLACE", "SELECT", "UPDATE", "VACUUM");

    private SqliteSqlGuards() {
    }

    /**
     * Validates a name embedded into a keyword position (collation, charset) against a
     * conservative allow-list. Returns the name unchanged when safe; throws otherwise
     * (fail closed).
     *
     * @throws IllegalArgumentException if the name contains unexpected characters
     */
    public static String requireSafeName(String name, String what) {
        if (name == null || !SAFE_NAME.matcher(name).matches()) {
            throw new IllegalArgumentException("Unsafe " + what + " name: " + name);
        }
        return name;
    }

    /**
     * Validates a free-text column type name before it is embedded into generated DDL.
     * Returns the type name unchanged when it matches a conservative allow-list;
     * throws otherwise (fail closed).
     *
     * @throws IllegalArgumentException if the type name contains unexpected characters
     */
    public static String requireSafeTypeName(String typeName) {
        String expression = StringUtils.trimToNull(typeName);
        if (expression == null) {
            return null;
        }
        scanExpression(expression, true, "column type");
        return expression;

View on GitHub (pinned to 5ee1e990e7)

Solutions

  1. Trim and restrict the name to ASCII letters, digits, and underscore.
  2. Pre-validate with the same [A-Za-z0-9_]+ pattern before calling the DDL builder.
  3. If a genuinely exotic collation name is required, register/handle it through a quoted path rather than the keyword-position path.

Example fix

// before
column.setCollationName("zh phonebook"); // space not allowed

// after
column.setCollationName("zh_phonebook");
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check the same allow-list the guard uses
private static final java.util.regex.Pattern SAFE = java.util.regex.Pattern.compile("[A-Za-z0-9_]+");
if (name != null && !SAFE.matcher(name).matches()) {
    throw new IllegalArgumentException("Name fails allow-list: " + name);
}

Type guard

static boolean isSafeSqliteName(String name) {
    return name != null && name.matches("[A-Za-z0-9_]+");
}

Prevention

When it happens

Trigger: Passing a collation or charset name containing spaces, dots, parentheses, quotes, or any non-[A-Za-z0-9_] character (e.g. 'NOCASE' is fine, but 'BINARY' with a trailing space, or a localized/custom collation like 'zh_phonebook' is fine whereas 'zh phonebook' is not). A null name also triggers it.

Common situations: User-typed or metadata-supplied collation with whitespace/punctuation; copy-paste introducing a stray character; a custom collation whose name contains a hyphen.

Related errors


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