OtterMind/Chat2DB · error · IllegalArgumentException

Unsafe column type name from metadata: {typeName}

Error message

Unsafe column type name from metadata: {typeName}

What it means

Thrown by H2SqlGuards.requireSafeTypeName when a non-null JDBC metadata column type name does not match the allow-list ^[A-Za-z][A-Za-z0-9_]*(\s+[A-Za-z][A-Za-z0-9_]*)*$. The guard prevents hostile or corrupt metadata from injecting SQL into generated DDL; null is permitted and returned unchanged.

Source

Thrown at chat2db-community-server/chat2db-community-plugins/chat2db-community-h2/src/main/java/ai/chat2db/plugin/h2/H2SqlGuards.java:50

        "(?i)^(?:NOW|RANDOM_UUID|UUID)\\(\\s*(?:\\d+)?\\s*\\)$");
    private static final Pattern TYPED_LITERAL = Pattern.compile(
        "(?i)^(?:DATE|TIME(?:\\s+WITH\\s+TIME\\s+ZONE)?|TIMESTAMP(?:\\s+WITH\\s+TIME\\s+ZONE)?|UUID|JSON|GEOMETRY)\\s+"
            + STRING_LITERAL_SOURCE + "$");
    private static final Pattern BINARY_LITERAL = Pattern.compile("(?i)^(?:X|BINARY)\\s*'[0-9A-F]*'$");
    private static final Pattern SEQUENCE_EXPRESSION = Pattern.compile(
        "(?i)^NEXT\\s+VALUE\\s+FOR\\s+" + IDENTIFIER_SOURCE + "(?:\\." + IDENTIFIER_SOURCE + ")?$");

    private H2SqlGuards() {
    }

    /**
     * Validates a column type name obtained from JDBC metadata before it is embedded
     * into generated DDL. Returns the type name unchanged when it matches the
     * allow-list; throws otherwise (fail closed).
     */
    public static String requireSafeTypeName(String typeName) {
        if (typeName != null && !SAFE_TYPE_NAME.matcher(typeName).matches()) {
            throw new IllegalArgumentException("Unsafe column type name from metadata: " + typeName);
        }
        return typeName;
    }

    /**
     * Reconstructs a type declaration from JDBC metadata without treating display width as a
     * type parameter. H2 reports values such as 64 for BIGINT and 26 for TIMESTAMP in
     * COLUMN_SIZE, but those values are not legal declarations for these types.
     */
    public static String renderMetadataType(String typeName, int dataType, int columnSize, int decimalDigits) {
        String safeTypeName = requireSafeTypeName(typeName);
        StringBuilder declaration = new StringBuilder(safeTypeName);
        switch (dataType) {
            case Types.CHAR:
            case Types.VARCHAR:
            case Types.NCHAR:
            case Types.NVARCHAR:
            case Types.BINARY:

View on GitHub (pinned to 5ee1e990e7)

Solutions

  1. Strip type parameters before calling requireSafeTypeName; renderMetadataType appends size/precision itself from columnSize/decimalDigits.
  2. If the type name legitimately needs special characters, whitelist it explicitly or handle that column outside the metadata-driven path.
  3. Upgrade/verify the H2 driver returns a clean TYPE_NAME for the column.

Example fix

// before
String t = "VARCHAR(255)"; // parentheses fail the allow-list
H2SqlGuards.requireSafeTypeName(t);

// after
String t = "VARCHAR"; // base name only
H2SqlGuards.renderMetadataType(t, Types.VARCHAR, 255, 0); // -> VARCHAR(255)
Defensive patterns

Strategy: validation

Validate before calling

private static final java.util.regex.Pattern SAFE_TYPE =
    java.util.regex.Pattern.compile("^[A-Za-z][A-Za-z0-9_]*(?:\\s+[A-Za-z][A-Za-z0-9_]*)*$");
static boolean isSafeH2TypeName(String t) {
    return t == null || SAFE_TYPE.matcher(t).matches();
}

Type guard

static String h2BaseTypeName(String t) {
    if (t == null) return null;
    int p = t.indexOf('(');
    return (p >= 0) ? t.substring(0, p).trim() : t; // caller renders params from metadata
}

Prevention

When it happens

Trigger: Calling requireSafeTypeName(typeName) or renderMetadataType(...) where typeName contains punctuation, digits-leading, parentheses, commas, or operators (e.g. "VARCHAR(255)", "INT,", "1TYPE"). Triggered when rebuilding a table DDL from H2 JDBC metadata that returns an unexpected TYPE_NAME.

Common situations: An H2 version or custom domain type whose TYPE_NAME includes parameter syntax; a corrupted metadata row; a column whose type name was manually edited to include size arguments.

Related errors


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