{"record":{"id":"9ff5348dc14f6b5e","repo":"iflytek/astron-agent","slug":"eng-protocol-validate-error","errorCode":"ENG_PROTOCOL_VALIDATE_ERROR","errorMessage":"{node.id} duplicate","messagePattern":"(.+?) duplicate","errorType":"error_code","errorClass":"CustomException","httpStatus":null,"severity":"error","filePath":"core/workflow/engine/dsl_engine.py","lineNumber":2100,"sourceCode":"        \"\"\"\n        Build workflow nodes.\n\n        :param span_context: Tracing span for observability\n        :return: Self for method chaining\n        \"\"\"\n        for node in self.sparkflow_dsl.nodes:\n\n            # Create engine node\n            spark_node_instance = self._create_engine_node(\n                node_id=node.id, span_context=span_context\n            )\n\n            # Handle special node types\n            self._handle_special_node_types(node, spark_node_instance)\n\n            # Check for duplicate nodes\n            if node.id in self.built_nodes:\n                raise CustomException(\n                    CodeEnum.ENG_PROTOCOL_VALIDATE_ERROR,\n                    err_msg=f\"{node.id} duplicate\",\n                )\n            self.built_nodes[node.id] = spark_node_instance\n\n        # Handle iteration engine nodes\n        self._build_iteration_engines()\n        self._build_loop_engines()\n\n        return self\n\n    def build_chains(self) -> \"WorkflowEngineBuilder\":\n        \"\"\"\n        Build execution chains.\n\n        :return: Self for method chaining\n        \"\"\"\n        self.chains = Chains(workflow_schema=self.sparkflow_dsl)","sourceCodeStart":2082,"sourceCodeEnd":2118,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/core/workflow/engine/dsl_engine.py#L2082-L2118","documentation":"During build_nodes, each DSL node id is registered in built_nodes. If the same node id appears more than once in the DSL, the second registration raises CustomException with ENG_PROTOCOL_VALIDATE_ERROR. Node ids must be unique across the graph.","triggerScenarios":"A WorkflowDSL passed to build_nodes (via WorkflowEngineBuilder or create_engine/create_debug_node) contains two nodes with identical id values.","commonSituations":"Copy-pasting nodes on the canvas without regenerating ids; merging two DSL versions; hand-crafted DSL JSON reusing ids; buggy frontend id generation.","solutions":["Ensure every node id in the DSL is unique before building","Regenerate ids for copied/duplicated nodes (usually a uuid per node)","Validate the DSL JSON before submitting it from the frontend"],"exampleFix":"// before\nnodes = [Node(id=\"llm:1\", ...), Node(id=\"llm:1\", ...)]\n// after\nids = [n.id for n in nodes]\nassert len(ids) == len(set(ids)), \"duplicate node ids in DSL\"","handlingStrategy":"validation","validationCode":"ids = [n.id for n in dsl.nodes]\nif len(ids) != len(set(ids)):\n    raise ValueError(\"duplicate node ids: \" + str([i for i in ids if ids.count(i) > 1]))","typeGuard":"def has_unique_node_ids(dsl) -> bool:\n    ids = [n.id for n in dsl.nodes]\n    return len(ids) == len(set(ids))","tryCatchPattern":"try:\n    engine = builder.build_nodes(span)\nexcept CustomException as e:\n    if \"duplicate\" in str(e):\n        report_dsl_error(\"Duplicate node ids in workflow\")\n    raise","preventionTips":["Always generate fresh uuids when copying nodes","Validate node-id uniqueness in the API layer before build","Include uniqueness checks in DSL schema validation"],"tags":["workflow","dsl","duplicate-id"],"backgroundTag":"schema-validation-failed","analyzedSha":"5e758547a83371a5a4b29dadf4ac03e8dd527635","analyzedAt":"2026-09-12T08:03:51.356Z","contentChangedAt":"2026-09-12T08:03:51.356Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}