{"record":{"id":"b8e79dae57a8b235","repo":"nathanmarz/storm","slug":"storm-conf-is-not-valid-must-be-json-serializable","errorCode":null,"errorMessage":"Storm conf is not valid. Must be json-serializable","messagePattern":"Storm conf is not valid\\. Must be json-serializable","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"storm-core/src/jvm/backtype/storm/StormSubmitter.java","lineNumber":75,"sourceCode":"    public static void submitTopology(String name, Map stormConf, StormTopology topology) throws AlreadyAliveException, InvalidTopologyException {\n        submitTopology(name, stormConf, topology, null);\n    }    \n    \n    /**\n     * Submits a topology to run on the cluster. A topology runs forever or until \n     * explicitly killed.\n     *\n     *\n     * @param name the name of the storm.\n     * @param stormConf the topology-specific configuration. See {@link Config}. \n     * @param topology the processing to execute.\n     * @param options to manipulate the starting of the topology\n     * @throws AlreadyAliveException if a topology with this name is already running\n     * @throws InvalidTopologyException if an invalid topology was submitted\n     */\n    public static void submitTopology(String name, Map stormConf, StormTopology topology, SubmitOptions opts) throws AlreadyAliveException, InvalidTopologyException {\n        if(!Utils.isValidConf(stormConf)) {\n            throw new IllegalArgumentException(\"Storm conf is not valid. Must be json-serializable\");\n        }\n        stormConf = new HashMap(stormConf);\n        stormConf.putAll(Utils.readCommandLineOpts());\n        Map conf = Utils.readStormConfig();\n        conf.putAll(stormConf);\n        try {\n            String serConf = JSONValue.toJSONString(stormConf);\n            if(localNimbus!=null) {\n                LOG.info(\"Submitting topology \" + name + \" in local mode\");\n                localNimbus.submitTopology(name, null, serConf, topology);\n            } else {\n                NimbusClient client = NimbusClient.getConfiguredClient(conf);\n                if(topologyNameExists(conf, name)) {\n                    throw new RuntimeException(\"Topology with name `\" + name + \"` already exists on cluster\");\n                }\n                submitJar(conf);\n                try {\n                    LOG.info(\"Submitting topology \" +  name + \" in distributed mode with conf \" + serConf);","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/nathanmarz/storm/blob/cdb116e942666973bc4eaa0df098d5bab82739e7/storm-core/src/jvm/backtype/storm/StormSubmitter.java#L57-L93","documentation":"StormSubmitter.submitTopology validates the stormConf Map with Utils.isValidConf, which requires every value to be JSON-serializable. If the conf contains non-serializable objects (custom classes, nulls in wrong places, etc.), it throws this IllegalArgumentException before contacting Nimbus.","triggerScenarios":"Putting non-JSON-serializable values into the topology conf Map (e.g. arbitrary Java objects, streams, complex types) and calling StormSubmitter.submitTopology / submitTopologyWithProgressBar.","commonSituations":"Programmatically building conf with typed objects (Date, custom config beans); reading conf from code that mixes Object values; injecting per-topology settings that aren't primitives/collections.","solutions":["Replace non-serializable values in stormConf with JSON primitives (String, Number, Boolean, Map, List).","Call Utils.isValidConf(conf) yourself before submitting to catch the bad key early.","If you need complex objects in a bolt, pass them via component configuration alternatives or serialize them yourself.","Print/inspect the conf Map and check each value's type."],"exampleFix":"// before\nconf.put(\"my.custom.object\", new MyConfigBean());\n// after\nconf.put(\"my.custom.object\", myBean.toJsonString());","handlingStrategy":"try-catch","validationCode":"// call before submitTopology\nif (!Utils.isValidConf(myConf)) {\n    for (Map.Entry e : ((Map) myConf).entrySet()) {\n        try { new Gson().toJsonTree(e.getValue()); }\n        catch (Exception ex) { System.err.println(\"Non-serializable conf value at key: \" + e.getKey()); }\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    StormSubmitter.submitTopology(name, conf, topology);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"json-serializable\")) {\n        log.error(\"Conf has non-JSON-serializable values; sanitize conf\", e);\n    } else { throw e; }\n}","preventionTips":["Restrict conf values to String/Number/Boolean/Map/List.","Never pass domain objects in topology conf; serialize them to strings.","Validate conf in CI with Utils.isValidConf."],"tags":["serialization","json","storm-config"],"backgroundTag":"json-serialization-failed","analyzedSha":"cdb116e942666973bc4eaa0df098d5bab82739e7","analyzedAt":"2026-09-12T14:30:00.714Z","contentChangedAt":"2026-09-12T14:30:00.714Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}