{"record":{"id":"31af54aefeff1949","repo":"ScrapeGraphAI/Scrapegraph-ai","slug":"llm-configuration-must-include-an-api-key","errorCode":null,"errorMessage":"LLM configuration must include an 'api_key'.","messagePattern":"LLM configuration must include an 'api_key'\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"scrapegraphai/builders/graph_builder.py","lineNumber":67,"sourceCode":"        self.config = config\n        self.llm = self._create_llm(config[\"llm\"])\n        self.nodes_description = self._generate_nodes_description()\n        self.chain = self._create_extraction_chain()\n\n    def _create_llm(self, llm_config: dict):\n        \"\"\"\n        Creates an instance of the OpenAI class with the provided language model configuration.\n\n        Returns:\n            OpenAI: An instance of the OpenAI class.\n\n        Raises:\n            ValueError: If 'api_key' is not provided in llm_config.\n        \"\"\"\n        llm_defaults = {\"temperature\": 0, \"streaming\": True}\n        llm_params = {**llm_defaults, **llm_config}\n        if \"api_key\" not in llm_params:\n            raise ValueError(\"LLM configuration must include an 'api_key'.\")\n\n        if \"gpt-\" in llm_params[\"model\"]:\n            return ChatOpenAI(llm_params)\n        elif \"gemini\" in llm_params[\"model\"]:\n            try:\n                from langchain_google_genai import ChatGoogleGenerativeAI\n            except ImportError:\n                raise ImportError(\n                    \"langchain_google_genai is not installed. Please install it using 'pip install langchain-google-genai'.\"\n                )\n            return ChatGoogleGenerativeAI(llm_params)\n        elif \"ernie\" in llm_params[\"model\"]:\n            return ErnieBotChat(llm_params)\n        raise ValueError(\"Model not supported\")\n\n    def _generate_nodes_description(self):\n        \"\"\"\n        Generates a string description of all available nodes and their arguments.","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/ScrapeGraphAI/Scrapegraph-ai/blob/532dfffbf6ee823a6c9cf8cfedc24a93bf026780/scrapegraphai/builders/graph_builder.py#L49-L85","documentation":"GenerateAnswerNode raises this ValueError when none of the state keys it checks (typically 'parsed_doc', 'doc', or 'content', whichever are configured) contain anything to summarize. It means the answer-generation step was reached without any scraped/parseed document ever being stored in the graph state, usually because the upstream fetch or parse node failed, was skipped, or wrote to a different key.","triggerScenarios":"Running a graph whose GenerateAnswerNode input_keys expect 'doc'/'parsed_doc'/'content' while the upstream node stored its output under a different key, or the fetcher returned an empty document (e.g. JS-only page rendered to nothing), or the parse node errored and the graph continued with empty state.","commonSituations":"Misconfigured graph where FetchNode/ParseNode output keys don't match GenerateAnswerNode input keys; a ChromiumLoader failing silently on heavy-JS sites; changing the parse framework (e.g. mongodb/vault integrations) so state['doc'] is never populated; reusing a custom node that forgets to write its result into state.","solutions":["Inspect the state right before answer generation (log state.keys() and state.get('doc')) to see which keys exist and whether any content was produced.","Verify the upstream FetchNode/ParseNode actually ran and that their output key matches an input key of GenerateAnswerNode in your graph edge definition.","If the page content is empty, fix the fetcher (headless_prompt, wait times, or use ChromiumLoader for JS-heavy pages).","As a last resort, pre-populate state['doc'] or state['content'] yourself with the text you want summarized."],"exampleFix":"# before\ngraph = SmartScraperGraph(\n    prompt=\"Summarize\",\n    source=\"https://example.com\",\n    config=graph_config,\n)\n\n# after: make sure fetch/parse ran and keys align; or seed state manually\nstate = graph.initial_state\nstate[\"doc\"] = \"your document text\"  # only if you bypass the fetch step","handlingStrategy":"validation","validationCode":"required = {\"doc\", \"parsed_doc\", \"content\"}\nhas_content = any(state.get(k) for k in required)\nif not has_content:\n    raise RuntimeError(\"Fetch/parse produced no content; check upstream nodes\") before running the answer step","typeGuard":"def has_scrapable_content(state: dict) -> bool:\n    return bool(state.get(\"doc\") or state.get(\"parsed_doc\") or state.get(\"content\"))","tryCatchPattern":"try:\n    result = graph.run()\nexcept ValueError as e:\n    if \"No content found\" in str(e):\n        # log state keys, retry with a different loader (e.g. ChromiumLoader)\n        ...","preventionTips":["Log state.keys() and content lengths after the parse step when developing a new graph.","Keep FetchNode/ParseNode output keys aligned with GenerateAnswerNode input_keys in custom graphs.","Use ChromiumLoader for JavaScript-heavy pages so the parsed document is not empty."],"tags":["state","missing-content","graph-pipeline","validation"],"backgroundTag":"missing-state-key","analyzedSha":"532dfffbf6ee823a6c9cf8cfedc24a93bf026780","analyzedAt":"2026-08-28T15:19:38.821Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}