apache/pulsar · error · IllegalArgumentException

Failed to parse tlsFactoryConfig as a JSON object

Error message

Failed to parse tlsFactoryConfig as a JSON object

What it means

parseFactoryConfig parses the tlsFactoryConfig string, accepting either a JSON object mapping strings to strings or a comma-separated key=value list. If the string looks like JSON but cannot be parsed into Map<String,String>, an IllegalArgumentException is thrown. The config string is therefore malformed.

Source

Thrown at pulsar-client/src/main/java/org/apache/pulsar/client/impl/tls/ClientTlsFactorySupport.java:367

     * <code>{</code> is parsed as a JSON object; otherwise it is parsed as a comma-separated
     * {@code key=value} list.
     *
     * @param tlsFactoryConfig the configured factory params (may be null/blank)
     * @return an immutable params map (possibly empty)
     */
    static Map<String, String> parseFactoryConfig(String tlsFactoryConfig) {
        if (StringUtils.isBlank(tlsFactoryConfig)) {
            return Map.of();
        }
        String trimmed = tlsFactoryConfig.trim();
        if (trimmed.startsWith("{")) {
            try {
                Map<String, String> parsed = ObjectMapperFactory.getMapper().reader()
                        .forType(new TypeReference<Map<String, String>>() {})
                        .readValue(trimmed);
                return parsed == null ? Map.of() : Map.copyOf(parsed);
            } catch (Exception e) {
                throw new IllegalArgumentException("Failed to parse tlsFactoryConfig as a JSON object", e);
            }
        }
        Map<String, String> map = new LinkedHashMap<>();
        for (String pair : trimmed.split(",")) {
            String entry = pair.trim();
            if (entry.isEmpty()) {
                continue;
            }
            int eq = entry.indexOf('=');
            if (eq < 0) {
                map.put(entry, "");
            } else {
                map.put(entry.substring(0, eq).trim(), entry.substring(eq + 1).trim());
            }
        }
        return Map.copyOf(map);
    }

View on GitHub (pinned to 820761864e)

Solutions

  1. Provide valid JSON: {"key1":"value1","key2":"value2"} with quoted keys and values.
  2. Or use the simple comma form: key1=value1,key2=value2.
  3. Validate the string with a JSON linter / JSON.parse before putting it in config.
  4. Check shell/env quoting so double quotes are not stripped before the client sees the value.

Example fix

// before
String config = "{keyA: valueA}"; // invalid JSON
// after
String config = "{\"keyA\":\"valueA\"}"; // or "keyA=valueA"
Defensive patterns

Strategy: validation

Validate before calling

String trimmed = tlsFactoryConfig.trim();
if (trimmed.startsWith("{")) {
    try { new ObjectMapper().readValue(trimmed, new TypeReference<Map<String,String>>() {}); }
    catch (Exception e) { throw new IllegalArgumentException("tlsFactoryConfig is not valid JSON", e); }
}

Try / catch

try {
    applyTlsFactoryConfig(raw);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("Failed to parse tlsFactoryConfig")) {
        log.error("Use {\"k\":\"v\"} JSON or k=v,k2=v2 form", e);
    }
}

Prevention

When it happens

Trigger: Setting tlsFactoryConfig to invalid JSON such as "{key: value}" (unquoted keys), "{..." truncated JSON, or a value that is neither valid JSON nor a clean key=value list.

Common situations: Hand-editing the config with unquoted JSON keys; YAML/shell quoting stripping double quotes so only braces remain; mixing JSON and comma-list syntax; trailing commas.

Understand the failure class

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/af0f433bc74efe62. Report an issue: GitHub.