{"record":{"id":"47ca17d343cd95fc","repo":"apache/pulsar","slug":"cannot-parse-sink-config","errorCode":null,"errorMessage":"Cannot parse sink-config","messagePattern":"Cannot parse sink-config","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdSinks.java","lineNumber":575,"sourceCode":"                resources.setRam(ram);\n            }\n\n            if (disk != null) {\n                if (resources == null) {\n                    resources = new Resources();\n                }\n                resources.setDisk(disk);\n            }\n            if (resources != null) {\n                sinkConfig.setResources(resources);\n            }\n\n            try {\n                if (null != sinkConfigString) {\n                    sinkConfig.setConfigs(parseConfigs(sinkConfigString));\n                }\n            } catch (Exception ex) {\n                throw new IllegalArgumentException(\"Cannot parse sink-config\", ex);\n            }\n\n            if (autoAck != null) {\n                sinkConfig.setAutoAck(autoAck);\n            }\n            if (timeoutMs != null) {\n                sinkConfig.setTimeoutMs(timeoutMs);\n            }\n            if (negativeAckRedeliveryDelayMs != null && negativeAckRedeliveryDelayMs > 0) {\n                sinkConfig.setNegativeAckRedeliveryDelayMs(negativeAckRedeliveryDelayMs);\n            }\n\n            if (customRuntimeOptions != null) {\n                sinkConfig.setCustomRuntimeOptions(customRuntimeOptions);\n            }\n\n            if (secretsString != null) {\n                Type type = new TypeToken<Map<String, Object>>() {}.getType();","sourceCodeStart":557,"sourceCodeEnd":593,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdSinks.java#L557-L593","documentation":"The --sink-config string passed to the sink command could not be parsed into a Map via parseConfigs (JSON parsing). The CLI wraps the underlying exception in an IllegalArgumentException with this message, so the sink is never created.","triggerScenarios":"Passing `--sink-config` with malformed JSON, e.g. `--sink-config 'key1=val1'` or single-quoted YAML instead of JSON like `'{\"key\":\"value\"}'`, or a JSON that parses to a non-map value.","commonSituations":"Shell quoting stripping inner double quotes (common with bash); using YAML config text in a JSON-expecting flag; trailing commas or unquoted keys in the JSON.","solutions":["Pass valid JSON: --sink-config '{\"key\":\"value\"}'","Use --sink-config-file pointing to a valid JSON file to avoid shell quoting issues","Validate the JSON with a parser (e.g. `jq .`) before running the command"],"exampleFix":"// before\n--sink-config 'roots=localhost'\n// after\n--sink-config '{\"roots\":\"localhost\"}'","handlingStrategy":"validation","validationCode":"const cfg = JSON.parse(sinkConfigString); // throws before invoking CLI if malformed\nif (typeof cfg !== 'object' || Array.isArray(cfg)) throw new Error('sink-config must be a JSON object');","typeGuard":"const isJsonObject = (s) => { try { return !Array.isArray(JSON.parse(s)) && typeof JSON.parse(s) === 'object'; } catch { return false; } };","tryCatchPattern":"try {\n  // run sinks create with --sink-config\n} catch (e) {\n  if (e.message.includes('Cannot parse sink-config')) { /* switch to --sink-config-file */ }\n  throw e;\n}","preventionTips":["Always pass JSON objects with quoted keys for --sink-config","Prefer --sink-config-file to avoid shell quoting pitfalls","Validate with `echo \"$CFG\" | jq .` before invoking the CLI"],"tags":["cli","json","configuration"],"backgroundTag":"invalid-json-config","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}