YunaiV/yudao-cloud · error · UnexpectedLiquibaseException

Unknown boolean value: {}

Error message

Unknown boolean value: {}

What it means

Liquibase BooleanType.objectToSqlValue fails to recognize the given value as a boolean. String values are matched against 'true'/'false', '1'/'0', "b'0'"/'f' (case-insensitive) and the database's native true/false literals; anything else reaches the final else branch and throws UnexpectedLiquibaseException.

Source

Thrown at sql/dm/flowable-patch/src/main/java/liquibase/datatype/core/BooleanType.java:85

        }

        String returnValue;
        if (value instanceof String) {
            value = ((String) value).replaceAll("'", "");
            if ("true".equals(((String) value).toLowerCase(Locale.US)) || "1".equals(value) || "b'1'".equals(((String) value).toLowerCase(Locale.US)) || "t".equals(((String) value).toLowerCase(Locale.US)) || ((String) value).toLowerCase(Locale.US).equals(this.getTrueBooleanValue(database).toLowerCase(Locale.US))) {
                returnValue = this.getTrueBooleanValue(database);
            } else if ("false".equals(((String) value).toLowerCase(Locale.US)) || "0".equals(value) || "b'0'".equals(
                ((String) value).toLowerCase(Locale.US)) || "f".equals(((String) value).toLowerCase(Locale.US)) || ((String) value).toLowerCase(Locale.US).equals(this.getFalseBooleanValue(database).toLowerCase(Locale.US))) {
                returnValue = this.getFalseBooleanValue(database);
            } else if (database instanceof PostgresDatabase && Pattern.matches("b?([01])\\1*(::bit|::\"bit\")?", (String) value)) {
                returnValue = "b'"
                    + value.toString()
                    .replace("b", "")
                    .replace("\"", "")
                    .replace("::it", "")
                    + "'::\"bit\"";
            } else {
                throw new UnexpectedLiquibaseException("Unknown boolean value: " + value);
            }
        } else if (value instanceof Long) {
            if (Long.valueOf(1).equals(value)) {
                returnValue = this.getTrueBooleanValue(database);
            } else {
                returnValue = this.getFalseBooleanValue(database);
            }
        } else if (value instanceof Number) {
            if (value.equals(1) || "1".equals(value.toString()) || "1.0".equals(value.toString())) {
                returnValue = this.getTrueBooleanValue(database);
            } else {
                returnValue = this.getFalseBooleanValue(database);
            }
        } else if (value instanceof DatabaseFunction) {
            return value.toString();
        } else if (value instanceof Boolean) {
            if (((Boolean) value)) {
                returnValue = this.getTrueBooleanValue(database);

View on GitHub (pinned to 477be9dd49)

Solutions

  1. Use a canonical boolean string in the changeset: 'true'/'false', '1'/'0', or exactly the value the target database's getTrueBooleanValue()/getFalseBooleanValue() returns.
  2. Remove extra quotes/whitespace — pass true, not 'true' with embedded quotes.
  3. If the value is computed SQL, move it to defaultValueComputed instead of defaultValue.
  4. For database-specific literals, set the value in a changeset property conditioned per-database (context/dbms attribute).

Example fix

<!-- before -->
<column name="active" type="boolean" defaultValue="yes"/>

<!-- after -->
<column name="active" type="boolean" defaultValue="true"/>
Defensive patterns

Strategy: validation

Validate before calling

private static final Pattern OK = Pattern.compile("(?i)^(true|false|1|0|b'[01]'|t|f)$");
String v = String.valueOf(defaultValue).trim();
if (!OK.matcher(v).matches()) throw new IllegalArgumentException("Bad boolean literal: " + v);

Type guard

boolean isLiquibaseBoolean(Object v) {
    return v instanceof Boolean || v instanceof Number
        || (v instanceof String s && s.matches("(?i)(true|false|1|0|b'[01]'|t|f)"));
}

Try / catch

try { value = BooleanType.objectToSqlValue(raw, db); }
catch (UnexpectedLiquibaseException e) {
    if (e.getMessage().startsWith("Unknown boolean value")) value = raw == null ? null : String.valueOf(raw);
    else throw e;
}

Prevention

When it happens

Trigger: A changeset column default value or <constraints> boolean value passes a string that is not in the accepted set, e.g. defaultValue="yes", defaultValue="Y", defaultValueComputed misused for a plain string, or a dialect-specific literal (e.g. "t"' or "TRUE()") that the current database's getTrueBooleanValue() does not return.

Common situations: Porting a changelog written for one database (MySQL '1'/'0', PostgreSQL bit strings) to another (DM/Oracle expecting 'TRUE'/'FALSE' or 1/0); XML changelogs where defaultValueComputed is used instead of defaultValue; locale-specific or quoted literals like "'true'" (extra quotes).

Related errors


AI-assisted analysis of YunaiV/yudao-cloud@477be9dd49 (2026-08-14). Data as JSON: /api/errors/fd78f94d75d280d7. Report an issue: GitHub.