OtterMind/Chat2DB · error · IllegalArgumentException

Invalid SQL Server collation name: {collation}

Error message

Invalid SQL Server collation name: {collation}

What it means

Thrown by SqlServerSqlGuards.validateCollation when the collation is null or does not fully match the allow-list [A-Za-z0-9_]+. Collation names are embedded as bare (unquoted) tokens in generated DDL, so anything outside ASCII alphanumerics/underscore is rejected to block DDL injection (fail closed).

Source

Thrown at chat2db-community-server/chat2db-community-plugins/chat2db-community-sqlserver/src/main/java/ai/chat2db/plugin/sqlserver/SqlServerSqlGuards.java:49

            "NATIONAL CHAR",
            "NATIONAL CHAR VARYING",
            "NATIONAL CHARACTER",
            "NATIONAL CHARACTER VARYING");
    private static final Set<String> COLUMN_CLAUSE_KEYWORDS = Set.of(
            "CHECK", "CONSTRAINT", "DEFAULT", "ENCRYPTED", "IDENTITY", "MASKED",
            "PERSISTED", "REFERENCES", "ROWGUIDCOL", "SPARSE", "UNIQUE");

    private SqlServerSqlGuards() {
    }

    /**
     * Validates a collation name before it is embedded into generated DDL.
     * Returns the collation unchanged when it matches the allow-list; throws
     * otherwise (fail closed).
     */
    public static String validateCollation(String collation) {
        if (collation == null || !COLLATION_NAME_PATTERN.matcher(collation).matches()) {
            throw new IllegalArgumentException("Invalid SQL Server collation name: " + collation);
        }
        return collation;
    }

    /**
     * Accepts a SQL Server built-in or schema-qualified user-defined type,
     * with an optional balanced argument list such as {@code decimal(18, 2)},
     * {@code nvarchar(max)}, or {@code xml(CONTENT [dbo].[Collection])}.
     */
    public static String requireColumnTypeExpression(String columnType) {
        if (StringUtils.isBlank(columnType)) {
            throw invalid("column type", columnType);
        }
        String expression = columnType.trim();
        int argumentsStart = findArgumentsStart(expression);
        String typeName = argumentsStart < 0 ? expression : expression.substring(0, argumentsStart).trim();
        if (!isTypeName(typeName)) {
            throw invalid("column type", columnType);

View on GitHub (pinned to 5ee1e990e7)

Solutions

  1. Supply only ASCII letters, digits, and underscore (e.g. SQL_Latin1_General_CP1_CI_AS).
  2. Pre-validate the collation against [A-Za-z0-9_]+ before setting it on the column model.
  3. If null/empty collation is intended, omit the COLLATE clause instead of passing null.

Example fix

// before
column.setCollationName("Latin1_General CI AS"); // spaces
// -> Invalid SQL Server collation name

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

Strategy: validation

Validate before calling

private static final java.util.regex.Pattern COLL = java.util.regex.Pattern.compile("[A-Za-z0-9_]+");
if (collation != null && !COLL.matcher(collation).matches()) {
    throw new IllegalArgumentException("Reject collation: " + collation);
}

Type guard

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

Prevention

When it happens

Trigger: Passing a collation containing a space, dot, hyphen, parentheses, quote, or any non-[A-Za-z0-9_] character - e.g. 'Latin1_General_CI_AS' is valid, but 'Latin1 General' or a Windows collation with a locale suffix delimiter is not. A null collation also triggers it.

Common situations: User-supplied or JDBC-reported collation with unexpected punctuation; copy-paste introducing whitespace; an attempt to pass a collation ID rather than its name.

Related errors


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