{"record":{"id":"4147cdf9a4718192","repo":"ScrapeGraphAI/Scrapegraph-ai","slug":"node-type-must-be-node-or-conditional-node-go","errorCode":null,"errorMessage":"node_type must be 'node' or 'conditional_node', got '{node_type}'","messagePattern":"node_type must be 'node' or 'conditional_node', got '(.+?)'","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"scrapegraphai/nodes/base_node.py","lineNumber":65,"sourceCode":"\n    def __init__(\n        self,\n        node_name: str,\n        node_type: str,\n        input: str,\n        output: List[str],\n        min_input_len: int = 1,\n        node_config: Optional[dict] = None,\n    ):\n        self.node_name = node_name\n        self.input = input\n        self.output = output\n        self.min_input_len = min_input_len\n        self.node_config = node_config\n        self.logger = get_logger()\n\n        if node_type not in [\"node\", \"conditional_node\"]:\n            raise ValueError(\n                f\"node_type must be 'node' or 'conditional_node', got '{node_type}'\"\n            )\n        self.node_type = node_type\n\n    @abstractmethod\n    def execute(self, state: dict) -> dict:\n        \"\"\"\n        Execute the node's logic based on the current state and update it accordingly.\n\n        Args:\n            state (dict): The current state of the graph.\n\n        Returns:\n            dict: The updated state after executing the node's logic.\n        \"\"\"\n\n        pass\n","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/ScrapeGraphAI/Scrapegraph-ai/blob/532dfffbf6ee823a6c9cf8cfedc24a93bf026780/scrapegraphai/nodes/base_node.py#L47-L83","documentation":"BaseNode.__init__ validates its node_type argument against the two allowed values ('node' and 'conditional_node'); anything else — including None, 'Node', or a custom string — raises immediately when the node object is constructed. node_type determines how BaseGraph routes execution, so it cannot be free-form.","triggerScenarios":"Subclassing BaseNode (or ConditionalNode) and passing node_type='custom' / 'router' / 'conditionalNode'; passing node_type=None in a custom constructor; calling BaseNode(...) directly for testing.","commonSituations":"Writing custom nodes for the first time and guessing the type string; case mismatches ('Conditional_Node'); refactoring where a variable holding the type is unset.","solutions":["Use node_type='node' for standard nodes and 'conditional_node' for nodes that return the next node's name.","Usually the right fix is to inherit from Node or ConditionalNode in scrapegraphai.nodes, which set node_type for you.","Check for typos/case if passing a computed value."],"exampleFix":"# before\nclass MyNode(BaseNode):\n    def __init__(self, **kwargs):\n        super().__init__(node_type='conditional', **kwargs)\n\n# after\nfrom scrapegraphai.nodes import ConditionalNode\nclass MyNode(ConditionalNode):\n    pass","handlingStrategy":"validation","validationCode":"assert node_type in ('node', 'conditional_node'), f\"invalid node_type {node_type!r}\"","typeGuard":"def is_valid_node_type(t: str) -> bool:\n    return t in ('node', 'conditional_node')","tryCatchPattern":"try:\n    n = MyNode(node_type=nt, ...)\nexcept ValueError as e:\n    if 'node_type must be' in str(e):\n        nt = 'conditional_node' if is_router else 'node'\n        n = MyNode(node_type=nt, ...)\n    else:\n        raise","preventionTips":["Inherit from Node / ConditionalNode instead of BaseNode directly.","Never make node_type configurable/free-form in custom node factories.","Cover custom nodes with a construction unit test."],"tags":["node","enum-validation","constructor","custom-nodes"],"backgroundTag":"invalid-enum-value","analyzedSha":"532dfffbf6ee823a6c9cf8cfedc24a93bf026780","analyzedAt":"2026-08-28T15:19:38.821Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}