{"record":{"id":"c972a41af0a6d1b2","repo":"ZhuLinsen/daily_stock_analysis","slug":"ambiguous-json","errorCode":null,"errorMessage":"ambiguous_json","messagePattern":"ambiguous_json","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/analyzer.py","lineNumber":4384,"sourceCode":"    def _apply_placeholder_fill(self, result: AnalysisResult, missing_fields: List[str]) -> None:\n        \"\"\"Delegate to module-level apply_placeholder_fill.\"\"\"\n        apply_placeholder_fill(result, missing_fields)\n\n    def _extract_analysis_json_object(self, response_text: str) -> Tuple[str, Dict[str, Any]]:\n        \"\"\"Extract the single allowed JSON object from an LLM response.\"\"\"\n\n        text = response_text or \"\"\n        stripped = text.strip()\n        if not stripped:\n            raise ValueError(\"empty_response\")\n\n        fence_pattern = re.compile(\n            r\"```[ \\t]*(?P<lang>[A-Za-z0-9_-]*)[ \\t]*\\n?(?P<body>.*?)```\",\n            flags=re.DOTALL,\n        )\n        fenced_matches = list(fence_pattern.finditer(text))\n        if len(fenced_matches) > 1:\n            raise ValueError(\"ambiguous_json\")\n        if len(fenced_matches) == 1:\n            match = fenced_matches[0]\n            outside = (text[:match.start()] + text[match.end():]).strip()\n            if outside:\n                raise ValueError(\"ambiguous_json\")\n            fence_lang = (match.group(\"lang\") or \"\").strip().lower()\n            if fence_lang not in {\"\", \"json\"}:\n                raise ValueError(\"ambiguous_json\")\n            json_str = match.group(\"body\").strip()\n            data = self._load_analysis_json_candidate(json_str)\n            return json_str, data\n        if \"```\" in text:\n            raise ValueError(\"ambiguous_json\")\n\n        try:\n            data = self._load_analysis_json_candidate(stripped)\n        except json.JSONDecodeError as exc:\n            if self._contains_embedded_json_object(text):","sourceCodeStart":4366,"sourceCodeEnd":4402,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/src/analyzer.py#L4366-L4402","documentation":"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.","triggerScenarios":"_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.","commonSituations":"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.","solutions":["Tighten the analysis prompt: demand ONLY one fenced ```json block with no other code blocks, and forbid examples in the output.","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.","Retry generation with a corrective message when this error occurs, since the same model often complies on the second attempt."],"exampleFix":"# before (prompt)\n\"Think step by step, show any code samples, then give the JSON report in a fence.\"\n# model emits 2 fences -> ValueError: ambiguous_json\n\n# after (prompt)\n\"Respond with exactly one ```json fenced block containing the report object and nothing else.\"","handlingStrategy":"retry","validationCode":"import re\n\nFENCE = re.compile(r\"```[^`]*?```\", re.DOTALL)\n\ndef single_fence_only(text: str) -> bool:\n    return len(FENCE.findall(text or \"\")) <= 1","typeGuard":null,"tryCatchPattern":"for attempt in range(2):\n    try:\n        return extract(text)\n    except ValueError as e:\n        if str(e) == \"ambiguous_json\" and attempt == 0:\n            text = regenerate_with(\"Output exactly one ```json block and nothing else.\")\n            continue\n        raise","preventionTips":["Prompt for exactly one ```json fence; forbid other code blocks.","Avoid few-shot examples that demonstrate multiple fences.","Do not regex-pick one of several fences — fix the prompt instead."],"tags":["llm","parsing","json-extraction","ambiguity"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}