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
- Open the workflow in the console editor and remove/repair edges or variable references that point to the missing node
- Validate the DSL before execution: collect all referenced node IDs (edges, branches, variable sources) and diff against the node list
- Re-export/re-save the workflow so IDs are regenerated consistently
- 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
- Validate DSL integrity (all edge endpoints exist as nodes) before save and before run
- Block publishing workflows with dangling references in the editor
- Regenerate/normalize node IDs on import instead of trusting foreign IDs
- Add a DSL schema/migration check on version upgrades
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
- WorkflowDSL must have at least one node.
- Start node ( ) not found.
- ENG_PROTOCOL_VALIDATE_ERROR
- ENG_PROTOCOL_VALIDATE_ERROR
- END_NODE_SCHEMA_ERROR
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)