iflytek/astron-agent · error · CustomException

PROTOCOL_BUILD_ERROR

PROTOCOL_BUILD_ERROR

Error message

Node {node_id} does not exist

What it means

Raised by WorkflowDSL.check_nodes_exist when a node lookup by ID fails during DSL traversal/processing. The DSL is treated as a protocol document; a reference to a node ID that is absent from the node list means the workflow graph is malformed, so a PROTOCOL_BUILD_ERROR is thrown instead of returning null.

Solutions

  1. Open the workflow in the console editor and remove/repair edges or variable references that point to the missing node
  2. Validate the DSL before execution: collect all referenced node IDs (edges, branches, variable sources) and diff against the node list
  3. Re-export/re-save the workflow so IDs are regenerated consistently
  4. Check for version skew between the saved DSL and the running engine version

Example fix

// before: engine fails at runtime
engine.run(dsl)
// after: validate referenced nodes first
node_ids = {e.source_id for e in dsl.edges} | {e.target_id for e in dsl.edges}
missing = node_ids - {n.id for n in dsl.nodes}
if missing:
    raise ValueError(f"DSL references missing nodes: {missing}")
engine.run(dsl)
Defensive patterns

Strategy: validation

Validate before calling

const nodeIds = new Set(dsl.nodes.map(n => n.id));
const refs = [...dsl.edges.flatMap(e => [e.source_id, e.target_id])];
const missing = refs.filter(id => !nodeIds.has(id));
if (missing.length) throw new Error(`DSL references missing nodes: ${missing}`);

Type guard

function nodeExists(dsl, id) {
  return dsl.nodes.some(n => n.id === id);
}

Try / catch

try {
  dsl.check_nodes_exist(node_id)
} catch (CustomException e) when (e.code == PROTOCOL_BUILD_ERROR) {
  logger.error(`broken DSL reference: ${node_id}`);
  throw new WorkflowValidationError(e);
}

Prevention

When it happens

Trigger: Calling check_nodes_exist (or any code path that resolves node IDs, e.g. edge/variable resolution) with a node_id that is not present in self.nodes — typically a deleted node still referenced by an edge, variable reference, or branch target.

Common situations: Hand-edited or legacy DSL JSON where a node was removed but edges still point to it; importing a workflow exported from a different version where node IDs were regenerated; frontend sending stale node IDs after undo/delete.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


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

Appendix: source

Thrown at core/workflow/engine/entities/workflow_dsl.py:161

    # Node information
    nodes: List[Node]

    # Edge information
    edges: List[Edge]

    def check_nodes_exist(self, node_id: str) -> Node:
        """
        Check if a node exists in the workflow.

        :param node_id: ID of the node to check
        :return: Node object if found
        :raises CustomException: If node is not found
        """
        for node in self.nodes:
            if node.id == node_id:
                return node
        raise CustomException(
            CodeEnum.PROTOCOL_BUILD_ERROR, err_msg=f"Node {node_id} does not exist"
        )

    def extract_iteration_sub_dsl(self, iteration_node_id: str) -> "WorkflowDSL":
        """
        Extract the standalone DSL for an iteration subgraph.

        :param iteration_node_id: The iteration container node ID
        :return: WorkflowDSL containing only the iteration subgraph
        :raises CustomException: If the iteration node or subgraph is invalid
        """
        from workflow.engine.entities.chains import Chains

        chains = Chains(workflow_schema=self)
        chains.gen()

        iteration_chains = chains.iteration_chains.get(iteration_node_id)
        if iteration_chains is None:

View on GitHub (pinned to 5e758547a8)