ZhuLinsen/daily_stock_analysis · error · ValueError

empty_response

Error message

empty_response

What it means

ValueError('empty_response') raised by _extract_analysis_json_object when the LLM response text, after strip(), is empty. This parser expects exactly one JSON object (optionally in a single code fence) describing the AnalysisResult, and an empty body cannot contain one. Unlike the empty-completion case in the generation loop, this error propagates out of the extraction step rather than being retried per-model.

Source

Thrown at src/analyzer.py:4376

            prefix = "### 上一次输出如下,请在该输出基础上补齐缺失字段,并重新输出完整 JSON。不要省略已有字段:"
        return "\n\n".join([
            base_prompt,
            prefix,
            previous_output,
            complement,
        ])

    def _apply_placeholder_fill(self, result: AnalysisResult, missing_fields: List[str]) -> None:
        """Delegate to module-level apply_placeholder_fill."""
        apply_placeholder_fill(result, missing_fields)

    def _extract_analysis_json_object(self, response_text: str) -> Tuple[str, Dict[str, Any]]:
        """Extract the single allowed JSON object from an LLM response."""

        text = response_text or ""
        stripped = text.strip()
        if not stripped:
            raise ValueError("empty_response")

        fence_pattern = re.compile(
            r"```[ \t]*(?P<lang>[A-Za-z0-9_-]*)[ \t]*\n?(?P<body>.*?)```",
            flags=re.DOTALL,
        )
        fenced_matches = list(fence_pattern.finditer(text))
        if len(fenced_matches) > 1:
            raise ValueError("ambiguous_json")
        if len(fenced_matches) == 1:
            match = fenced_matches[0]
            outside = (text[:match.start()] + text[match.end():]).strip()
            if outside:
                raise ValueError("ambiguous_json")
            fence_lang = (match.group("lang") or "").strip().lower()
            if fence_lang not in {"", "json"}:
                raise ValueError("ambiguous_json")
            json_str = match.group("body").strip()
            data = self._load_analysis_json_candidate(json_str)

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Check the upstream completion: if content is empty, do not invoke extraction — branch to retry/re-fallback at the generation layer.
  2. Purge or overwrite cached empty analysis responses so they are never re-extracted.
  3. Fix mocks/tests to return a realistic JSON body.

Example fix

# before
text = cached_response.text  # ''
json_str, data = analyzer._extract_analysis_json_object(text)  # ValueError: empty_response

# after
if not (cached_response.text or '').strip():
    cached_response = regenerate()
json_str, data = analyzer._extract_analysis_json_object(cached_response.text)
Defensive patterns

Strategy: validation

Validate before calling

def has_extractable_text(text: str) -> bool:
    return bool((text or "").strip())

Try / catch

try:
    json_str, data = analyzer._extract_analysis_json_object(text)
except ValueError as e:
    if str(e) == "empty_response":
        text = regenerate_response()  # retry generation, not extraction
        json_str, data = analyzer._extract_analysis_json_object(text)
    else:
        raise

Prevention

When it happens

Trigger: Calling _extract_analysis_json_object('') or with whitespace-only / None-coerced text ('' after response_text or '' check). Typically the downstream half of a completion that returned empty content: generation's empty check was bypassed or the text was passed through from another source (mock, cached entry, plugin).

Common situations: Caching layer storing an empty analysis body and replaying it; tests or mocks returning '' as the assistant message; a prior pipeline stage 'sanitizing' the response into emptiness; refusal responses reduced to blank text.

Related errors


AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15). Data as JSON: /api/errors/aabd3c946fb2d893. Report an issue: GitHub.