{"record":{"id":"ebbd078ddb6c07af","repo":"nathanmarz/storm","slug":"must-declare-exactly-one-stream-from-last-bolt-in","errorCode":null,"errorMessage":"Must declare exactly one stream from last bolt in LinearDRPCTopology","messagePattern":"Must declare exactly one stream from last bolt in LinearDRPCTopology","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"storm-core/src/jvm/backtype/storm/drpc/LinearDRPCTopologyBuilder.java","lineNumber":152,"sourceCode":"                    prevId = PREPARE_ID;\n                } else {\n                    prevId = boltId(i-1);\n                }\n                for(InputDeclaration declaration: component.declarations) {\n                    declaration.declare(prevId, declarer);\n                }\n            }\n            if(i>0) {\n                declarer.directGrouping(boltId(i-1), Constants.COORDINATED_STREAM_ID); \n            }\n        }\n        \n        IRichBolt lastBolt = _components.get(_components.size()-1).bolt;\n        OutputFieldsGetter getter = new OutputFieldsGetter();\n        lastBolt.declareOutputFields(getter);\n        Map<String, StreamInfo> streams = getter.getFieldsDeclaration();\n        if(streams.size()!=1) {\n            throw new RuntimeException(\"Must declare exactly one stream from last bolt in LinearDRPCTopology\");\n        }\n        String outputStream = streams.keySet().iterator().next();\n        List<String> fields = streams.get(outputStream).get_output_fields();\n        if(fields.size()!=2) {\n            throw new RuntimeException(\"Output stream of last component in LinearDRPCTopology must contain exactly two fields. The first should be the request id, and the second should be the result.\");\n        }\n\n        builder.setBolt(boltId(i), new JoinResult(PREPARE_ID))\n                .fieldsGrouping(boltId(i-1), outputStream, new Fields(fields.get(0)))\n                .fieldsGrouping(PREPARE_ID, PrepareRequest.RETURN_STREAM, new Fields(\"request\"));\n        i++;\n        builder.setBolt(boltId(i), new ReturnResults())\n                .noneGrouping(boltId(i-1));\n        return builder.createTopology();\n    }\n    \n    private static String boltId(int index) {\n        return \"bolt\" + index;","sourceCodeStart":134,"sourceCodeEnd":170,"githubUrl":"https://github.com/nathanmarz/storm/blob/cdb116e942666973bc4eaa0df098d5bab82739e7/storm-core/src/jvm/backtype/storm/drpc/LinearDRPCTopologyBuilder.java#L134-L170","documentation":"LinearDRPCTopologyBuilder.createTopology inspects the last bolt's declared output streams; the framework pipes its single output stream into JoinResult. If the last bolt declares zero or multiple streams, a RuntimeException is thrown because the linear DRPC wiring is impossible.","triggerScenarios":"Implementing the final bolt's declareOutputFields with multiple outputFieldsDeclarer.stream(...) declarations, or none, in a LinearDRPCTopology.","commonSituations":"Refactoring a bolt to add a second (error/reporting) stream; copying a multi-stream bolt as the last stage; forgetting to declare any stream.","solutions":["Make the last bolt declare exactly one output stream (use plain declare(), not multiple stream() calls).","Move auxiliary streams (error streams etc.) to a bolt that is not last.","If multiple outputs are needed, add a final adapter bolt that merges them into one stream."],"exampleFix":"// before\npublic void declareOutputFields(OutputFieldsDeclarer declarer) {\n    declarer.declareStream(\"result\", new Fields(\"id\", \"result\"));\n    declarer.declareStream(\"errors\", new Fields(\"id\", \"msg\"));\n}\n// after\npublic void declareOutputFields(OutputFieldsDeclarer declarer) {\n    declarer.declare(new Fields(\"id\", \"result\"));\n}","handlingStrategy":"validation","validationCode":"// verify last bolt declares exactly one stream before createTopology\nOutputFieldsGetter g = new OutputFieldsGetter();\nlastBolt.declareOutputFields(g);\nif (g.getFieldsDeclaration().size() != 1) {\n    throw new IllegalStateException(\"Last bolt must declare exactly one stream\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["In the final bolt always use plain declare(), never stream().","Keep LinearDRPC topologies' last bolt minimal and dedicated to emitting the result.","Add a unit test calling createLocalTopology to catch declaration issues early."],"tags":["drpc","topology-definition","stream-declaration","storm"],"backgroundTag":"invalid-config-value","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"}