ZhuLinsen/daily_stock_analysis · error · ValueError

ambiguous_json

Error message

ambiguous_json

What it means

ValueError('ambiguous_json') raised by _extract_analysis_json_object when the response contains MORE THAN ONE fenced ``` code block. The parser's contract is a single JSON object; multiple fences mean the model emitted several candidates (or fenced prose plus JSON) and picking one arbitrarily could silently deserialize the wrong payload, so it refuses instead of guessing.

Source

Thrown at src/analyzer.py:4384

    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)
            return json_str, data
        if "```" in text:
            raise ValueError("ambiguous_json")

        try:
            data = self._load_analysis_json_candidate(stripped)
        except json.JSONDecodeError as exc:
            if self._contains_embedded_json_object(text):

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Tighten the analysis prompt: demand ONLY one fenced ```json block with no other code blocks, and forbid examples in the output.
  2. If extra fences are deterministic (e.g. always a known-language example first), strip them upstream before calling the extractor — but only with a deterministic rule, not a guess.
  3. Retry generation with a corrective message when this error occurs, since the same model often complies on the second attempt.

Example fix

# before (prompt)
"Think step by step, show any code samples, then give the JSON report in a fence."
# model emits 2 fences -> ValueError: ambiguous_json

# after (prompt)
"Respond with exactly one ```json fenced block containing the report object and nothing else."
Defensive patterns

Strategy: retry

Validate before calling

import re

FENCE = re.compile(r"```[^`]*?```", re.DOTALL)

def single_fence_only(text: str) -> bool:
    return len(FENCE.findall(text or "")) <= 1

Try / catch

for attempt in range(2):
    try:
        return extract(text)
    except ValueError as e:
        if str(e) == "ambiguous_json" and attempt == 0:
            text = regenerate_with("Output exactly one ```json block and nothing else.")
            continue
        raise

Prevention

When it happens

Trigger: _extract_analysis_json_object on text like "```json\n{...}\n```\nSome notes\n```\n{...}\n```" — any response where the fence regex finds 2+ matches. Common when the model explains its answer with example blocks or emits both a summary and the result as separate fences.

Common situations: Prompts that ask for 'reasoning then JSON in a code block' (model fences both); few-shot examples in the prompt teaching multi-fence output; model regressions/switches that add commentary fences around the answer.

Related errors


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