{"record":{"id":"81b09593982c5bde","repo":"unclecode/crawl4ai","slug":"llm-returned-an-empty-response","errorCode":null,"errorMessage":"LLM returned an empty response","messagePattern":"LLM returned an empty response","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"crawl4ai/extraction_strategy.py","lineNumber":1931,"sourceCode":"\n        for attempt in range(max_attempts):\n            try:\n                response = await aperform_completion_with_backoff(\n                    provider=llm_config.provider,\n                    prompt_with_variables=prompt,\n                    json_response=True,\n                    api_token=llm_config.api_token,\n                    base_url=llm_config.base_url,\n                    messages=messages,\n                    extra_args=kwargs,\n                )\n                if usage is not None:\n                    usage.completion_tokens += response.usage.completion_tokens\n                    usage.prompt_tokens += response.usage.prompt_tokens\n                    usage.total_tokens += response.usage.total_tokens\n                raw = response.choices[0].message.content\n                if not raw or not raw.strip():\n                    raise ValueError(\"LLM returned an empty response\")\n\n                schema = json.loads(_strip_markdown_fences(raw))\n                last_schema = schema\n            except json.JSONDecodeError as e:\n                # JSON parse failure — ask LLM to fix it\n                if not validate or attempt >= max_attempts - 1:\n                    raise Exception(f\"Failed to parse schema JSON: {str(e)}\")\n                messages.append({\"role\": \"assistant\", \"content\": raw})\n                messages.append({\"role\": \"user\", \"content\": (\n                    f\"Your response was not valid JSON. Parse error: {e}\\n\"\n                    \"Please return ONLY valid JSON, nothing else.\"\n                )})\n                continue\n            except Exception as e:\n                raise Exception(f\"Failed to generate schema: {str(e)}\")\n\n            # If validation is off, return immediately (zero overhead path)\n            if not validate:","sourceCodeStart":1913,"sourceCodeEnd":1949,"githubUrl":"https://github.com/unclecode/crawl4ai/blob/7e801521428ee12509994d39151006f64055ebe3/crawl4ai/extraction_strategy.py#L1913-L1949","documentation":"Raised in the generate_schema LLM loop when the completion returns choices[0].message.content that is empty or whitespace-only. Before JSON parsing, the code rejects blank responses as a ValueError — an empty completion is treated as a hard failure rather than retried in the same attempt cycle.","triggerScenarios":"The configured LLM endpoint returns an empty message: content-filtered responses, misconfigured local/OpenAI-compatible servers, token limits collapsing output to nothing, or a provider returning only whitespace.","commonSituations":"Using a local/incompatible OpenAI-compatible server whose response shape yields empty content; content-filtered or safety-blocked completions; API quota issues returning empty bodies; wrong base_url routing to a non-chat endpoint.","solutions":["Verify the same prompt works with a direct LLM call using your llm_config credentials","Check provider/base_url in LLMLConfig — an empty content often means you hit the wrong endpoint","Try a different model or provider to rule out model-specific empty-response behavior","Catch the ValueError and retry generate_schema — transient empty completions do occur"],"exampleFix":"// before\nschema = await JsonElementExtractionStrategy.generate_schema(\n    html=html, llm_config=LLMConfig(provider=\"local/empty-server\"))\n# ValueError: LLM returned an empty response\n\n// after\nfor attempt in range(3):\n    try:\n        schema = await JsonElementExtractionStrategy.generate_schema(\n            html=html, llm_config=LLMConfig(provider=\"openai/gpt-4o\", api_token=tok))\n        break\n    except ValueError as e:\n        if \"empty response\" not in str(e) or attempt == 2:\n            raise","handlingStrategy":"retry","validationCode":"# verify the LLM endpoint returns non-empty JSON-style completions first\nfrom crawl4ai import aperform_completion_with_backoff  # or your provider SDK\nresp = await aperform_completion_with_backoff(\n    provider=cfg.provider, api_token=cfg.api_token,\n    prompt='Reply with the single word: OK')\nassert resp.choices[0].message.content.strip(), \"endpoint returns empty content\"","typeGuard":null,"tryCatchPattern":"import asyncio\n\nfor i in range(3):\n    try:\n        schema = await JsonElementExtractionStrategy.generate_schema(html=h, llm_config=cfg)\n        break\n    except ValueError as e:\n        if \"empty response\" not in str(e) or i == 2:\n            raise\n        await asyncio.sleep(2 ** i)","preventionTips":["Smoke-test the LLM endpoint with a trivial prompt before schema generation","Verify provider/base_url/api_token in LLMLConfig point at a real chat-completions endpoint","Retry once or twice — transient empty completions do occur"],"tags":["extraction","schema-generation","llm","empty-response"],"backgroundTag":null,"analyzedSha":"7e801521428ee12509994d39151006f64055ebe3","analyzedAt":"2026-08-14T20:46:20.673Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}