{"record":{"id":"20718a04b3d75624","repo":"ScrapeGraphAI/Scrapegraph-ai","slug":"adjacent-state-keys-found-without-an-operator-betw","errorCode":null,"errorMessage":"Adjacent state keys found without an operator between them.","messagePattern":"Adjacent state keys found without an operator between them\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"scrapegraphai/nodes/base_node.py","lineNumber":164,"sourceCode":"        Returns:\n            List[str]: A list of key names that match the input keys expression logic.\n\n        Raises:\n            ValueError: If the expression is invalid or if no state keys match the expression.\n        \"\"\"\n\n        if not expression:\n            raise ValueError(\"Empty expression.\")\n\n        pattern = (\n            r\"\\b(\"\n            + \"|\".join(re.escape(key) for key in state.keys())\n            + r\")(\\b\\s*\\b)(\"\n            + \"|\".join(re.escape(key) for key in state.keys())\n            + r\")\\b\"\n        )\n        if re.search(pattern, expression):\n            raise ValueError(\n                \"Adjacent state keys found without an operator between them.\"\n            )\n\n        expression = expression.replace(\" \", \"\")\n\n        if (\n            expression[0] in \"&|\"\n            or expression[-1] in \"&|\"\n            or \"&&\" in expression\n            or \"||\" in expression\n            or \"&|\" in expression\n            or \"|&\" in expression\n        ):\n            raise ValueError(\"Invalid operator usage.\")\n\n        open_parentheses = close_parentheses = 0\n        for i, char in enumerate(expression):\n            if char == \"(\":","sourceCodeStart":146,"sourceCodeEnd":182,"githubUrl":"https://github.com/ScrapeGraphAI/Scrapegraph-ai/blob/532dfffbf6ee823a6c9cf8cfedc24a93bf026780/scrapegraphai/nodes/base_node.py#L146-L182","documentation":"Raised by BaseNode._parse_input_keys when the boolean expression used to declare a node's input contains two state key names directly adjacent to each other with no '&' or '|' operator between them. ScrapeGraphAI parses these expressions to compute which state keys a node consumes, so a malformed expression cannot be evaluated. The check is done with a regex that finds word-boundary-separated key names next to each other.","triggerScenarios":"Passing node_config input like \"key1 key2\" or \"parsed_docs user_prompt\" (space-separated keys) instead of \"key1 & key2\". Also happens after spaces are stripped if two key names appear back-to-back, e.g. a key whose name ends with another key's name boundary-adjacent in the expression.","commonSituations":"Writing custom nodes and copying an input string that omits operators; renaming state keys so two adjacent tokens both match state key names; using commas instead of '|' between keys.","solutions":["Join all keys with explicit operators, e.g. input=\"key1 & key2\" or \"key1 | key2\"","If you intended both keys to be required, use '&' between every pair","Check for accidental concatenation of key names without separators in the input string","Verify the state keys you reference actually exist in the graph's state schema"],"exampleFix":"// before\nnode = MyNode(node_config={\"input\": \"parsed_docs user_prompt\"})\n// after\nnode = MyNode(node_config={\"input\": \"parsed_docs & user_prompt\"})","handlingStrategy":"validation","validationCode":"import re\ndef valid_input_expression(expr: str, state_keys) -> bool:\n    keys = [re.escape(k) for k in state_keys]\n    pat = r'(?:' + '|'.join(keys) + r')\\b\\s*\\b(?:' + '|'.join(keys) + r')\\b'\n    return not re.search(pat, expr)","typeGuard":null,"tryCatchPattern":"try:\n    node.get_input_keys(state)\nexcept ValueError as e:\n    if 'Adjacent state keys' in str(e):\n        # fix expression: join keys with '&'\n        fixed = ' & '.join(expr.split())\n    else:\n        raise","preventionTips":["Always join multiple input keys with explicit '&' or '|'","Lint node input strings at graph-construction time"],"tags":["scrapegraphai","node-config","expression-parsing","boolean-expression"],"backgroundTag":"expression-syntax-invalid","analyzedSha":"532dfffbf6ee823a6c9cf8cfedc24a93bf026780","analyzedAt":"2026-08-28T15:19:38.821Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}