iflytek/astron-agent · error · CustomException

ENG_PROTOCOL_VALIDATE_ERROR

ENG_PROTOCOL_VALIDATE_ERROR

Error message

{node.id} duplicate

What it means

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.

Solutions

  1. Ensure every node id in the DSL is unique before building
  2. Regenerate ids for copied/duplicated nodes (usually a uuid per node)
  3. Validate the DSL JSON before submitting it from the frontend

Example fix

// before
nodes = [Node(id="llm:1", ...), Node(id="llm:1", ...)]
// after
ids = [n.id for n in nodes]
assert len(ids) == len(set(ids)), "duplicate node ids in DSL"
Defensive patterns

Strategy: validation

Validate before calling

ids = [n.id for n in dsl.nodes]
if len(ids) != len(set(ids)):
    raise ValueError("duplicate node ids: " + str([i for i in ids if ids.count(i) > 1]))

Type guard

def has_unique_node_ids(dsl) -> bool:
    ids = [n.id for n in dsl.nodes]
    return len(ids) == len(set(ids))

Try / catch

try:
    engine = builder.build_nodes(span)
except CustomException as e:
    if "duplicate" in str(e):
        report_dsl_error("Duplicate node ids in workflow")
    raise

Prevention

When it happens

Trigger: A WorkflowDSL passed to build_nodes (via WorkflowEngineBuilder or create_engine/create_debug_node) contains two nodes with identical id values.

Common situations: Copy-pasting nodes on the canvas without regenerating ids; merging two DSL versions; hand-crafted DSL JSON reusing ids; buggy frontend id generation.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of iflytek/astron-agent@5e758547a8 (2026-09-12). Data as JSON: /api/errors/9ff5348dc14f6b5e. Report an issue: GitHub.

Appendix: source

Thrown at core/workflow/engine/dsl_engine.py:2100

        """
        Build workflow nodes.

        :param span_context: Tracing span for observability
        :return: Self for method chaining
        """
        for node in self.sparkflow_dsl.nodes:

            # Create engine node
            spark_node_instance = self._create_engine_node(
                node_id=node.id, span_context=span_context
            )

            # Handle special node types
            self._handle_special_node_types(node, spark_node_instance)

            # Check for duplicate nodes
            if node.id in self.built_nodes:
                raise CustomException(
                    CodeEnum.ENG_PROTOCOL_VALIDATE_ERROR,
                    err_msg=f"{node.id} duplicate",
                )
            self.built_nodes[node.id] = spark_node_instance

        # Handle iteration engine nodes
        self._build_iteration_engines()
        self._build_loop_engines()

        return self

    def build_chains(self) -> "WorkflowEngineBuilder":
        """
        Build execution chains.

        :return: Self for method chaining
        """
        self.chains = Chains(workflow_schema=self.sparkflow_dsl)

View on GitHub (pinned to 5e758547a8)