MyCATApache/Mycat-Server · error · ConfigException

Unterminated property

Error message

Unterminated property: ${text}

What it means

ConfigUtil.filter performs ${property} placeholder substitution in config text. When it finds '${' but never a closing '}', it throws ConfigException indicating the property reference is unterminated. This protects against silently treating malformed placeholders as literal text.

Solutions

  1. Locate the '${' occurrence in the offending config value and add the missing closing '}'
  2. If the literal text '${' is intended, escape or reword it so the filter does not see a placeholder
  3. Validate all config values contain balanced '${...}' pairs before loading

Example fix

// before (in config value)
url=jdbc:mysql://${host:3306/db
// after
url=jdbc:mysql://${host}:3306/db
Defensive patterns

Strategy: validation

Validate before calling

long open = text.chars().filter(c -> c == '$').count();
long close = text.chars().filter(c -> c == '}').count();
if (open != close) throw new ConfigException("unbalanced ${} in: " + text);

Try / catch

try { filtered = ConfigUtil.filter(text, props); } catch (ConfigException e) { log.error("bad placeholder in config: {}", e.getMessage()); throw e; }

Prevention

When it happens

Trigger: A config value contains '${' without a matching '}', e.g. "password=${db_pass" or a lone '${' from an accidental paste or shell-variable expansion that was stripped.

Common situations: Hand-edited mycat config files (server.xml/schema.xml) with a typo in a ${...} expression; environment interpolation removing the closing brace; copy-paste truncation.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of MyCATApache/Mycat-Server@65f8d8beb7 (2026-09-11). Data as JSON: /api/errors/2985e69dbf09861a. Report an issue: GitHub.

Appendix: source

Thrown at src/main/java/io/mycat/config/util/ConfigUtil.java:75

    }

    public static String filter(String text, Properties properties) {
        StringBuilder s = new StringBuilder();
        int cur = 0;
        int textLen = text.length();
        int propStart = -1;
        int propStop = -1;
        String propName = null;
        String propValue = null;
        for (; cur < textLen; cur = propStop + 1) {
            propStart = text.indexOf("${", cur);
            if (propStart < 0) {
                break;
            }
            s.append(text.substring(cur, propStart));
            propStop = text.indexOf("}", propStart);
            if (propStop < 0) {
                throw new ConfigException("Unterminated property: " + text.substring(propStart));
            }
            propName = text.substring(propStart + 2, propStop);
            propValue = properties.getProperty(propName);
            if (propValue == null) {
                s.append("${").append(propName).append('}');
            } else {
                s.append(propValue);
            }
        }
        return s.append(text.substring(cur)).toString();
    }

    public static Document getDocument(final InputStream dtd, InputStream xml) throws ParserConfigurationException,
            SAXException, IOException {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setValidating(true);
        factory.setNamespaceAware(false);
        DocumentBuilder builder = factory.newDocumentBuilder();

View on GitHub (pinned to 65f8d8beb7)