{"record":{"id":"fc1a556751593630","repo":"zylon-ai/private-gpt","slug":"generator-did-not-return-valid-json","errorCode":null,"errorMessage":"Generator did not return valid JSON.","messagePattern":"Generator did not return valid JSON\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ui/index.html","lineNumber":4812,"sourceCode":"      ].join(\"\\n\");\n    }\n\n    function extractJsonObject(text) {\n      const trimmed = String(text || \"\").trim();\n      if (!trimmed) throw new Error(\"Empty response from generator.\");\n      try {\n        return JSON.parse(trimmed);\n      } catch {}\n      const fenced = trimmed.match(/```(?:json)?\\s*([\\s\\S]*?)```/i);\n      if (fenced) {\n        try { return JSON.parse(fenced[1].trim()); } catch {}\n      }\n      const start = trimmed.indexOf(\"{\");\n      const end = trimmed.lastIndexOf(\"}\");\n      if (start >= 0 && end > start) {\n        return JSON.parse(trimmed.slice(start, end + 1));\n      }\n      throw new Error(\"Generator did not return valid JSON.\");\n    }\n\n    function normalizeAppearanceSuggestion(candidate, fallbackBrief = \"\") {\n      const appearance = {\n        ...DEFAULT_APPEARANCE,\n        ...candidate,\n        brief: typeof candidate?.brief === \"string\" && candidate.brief.trim() ? candidate.brief.trim() : fallbackBrief,\n        themeMode: [\"light\", \"dark\", \"auto\"].includes(candidate?.themeMode) ? candidate.themeMode : DEFAULT_APPEARANCE.themeMode,\n        palette: {\n          ...DEFAULT_APPEARANCE.palette,\n          ...(candidate?.palette || {})\n        },\n        features: {\n          ...DEFAULT_APPEARANCE.features,\n          ...(candidate?.features || {})\n        }\n      };\n      [\"accent\", \"secondary\", \"surface\", \"background\"].forEach(key => {","sourceCodeStart":4794,"sourceCodeEnd":4830,"githubUrl":"https://github.com/zylon-ai/private-gpt/blob/4a030776a31a901ad80b1bf4d7faa2c1a367efbb/ui/index.html#L4794-L4830","documentation":"This RuntimeError is raised by update_anthropic_version in scripts/update_claude_specs.py (line 71). It reads pyproject.toml, applies re.subn with the exact pattern \"anthropic>=[\\d.]+\" (a double-quoted dependency string whose version is only digits and dots), and aborts when nothing matched. It means the anthropic floor-version dependency the script is designed to bump no longer appears in pyproject.toml in that exact literal form (today it is 'anthropic>=0.120.2' on line 54 under the llm-anthropic extra).","triggerScenarios":"Shapes of pyproject.toml that yield count == 0: (1) the specifier operator changed, e.g. 'anthropic~=0.120.2' or 'anthropic==0.120.2'; (2) an upper bound or marker was added so characters other than [\\d.] precede the closing quote, e.g. \"anthropic>=0.120.2,<1\" — the class [\\d.] stops at the comma and the required '\"' never follows; (3) whitespace inside the string like \"anthropic >= 0.120.2\"; (4) single-quoted (literal) TOML strings; (5) the dependency was dropped from the llm-anthropic extra or moved/renamed. Like error 440, it only fires when the PyPI latest version differs from current_anthropic_version()'s match, via main() at line 109.","commonSituations":"Hit in the scheduled update-claude-specs CI job after a maintainer tightens the dependency range (adds <1 upper bound — a common breaking-change policy), switches to PEP 440 compatible release ~=, or reformats pyproject.toml with a tool that normalizes quotes/spacing. Silent until Anthropic publishes a new version, at which point the automation fails instead of opening its PR.","solutions":["Check pyproject.toml line 54 and restore the exact shape \"anthropic>=X.Y.Z\" (double quotes, no spaces, digits-and-dots-only version) inside the llm-anthropic extra.","If an upper bound is intentionally kept (e.g. \"anthropic>=0.120.2,<1\"), broaden the regex in scripts/update_claude_specs.py line 66 to r'\"anthropic>=[\\d.]+[^\"]*\"' and update the reader regex at line 44 to r'\"anthropic>=([\\d.]+)' so both sides handle the suffix.","If the operator or quote style changed project-wide, align both patterns (lines 44 and 66) with the new convention — reader and writer must stay in sync or the script will report '<not found>' and then raise.","Re-run python scripts/update_claude_specs.py and verify it bumps the floor and runs 'uv lock' successfully."],"exampleFix":"# pyproject.toml [project.optional-dependencies] llm-anthropic — before (upper bound breaks [\\d.]+ then '\"')\nllm-anthropic = [\n    \"langchain-anthropic>=1.4.3\",\n    \"anthropic>=0.120.2,<1\",\n]\n\n# after (plain floor the script understands)\nllm-anthropic = [\n    \"langchain-anthropic>=1.4.3\",\n    \"anthropic>=0.120.2\",\n]\n\n# or, to keep the upper bound, change scripts/update_claude_specs.py:66 to:\n#     r'\"anthropic>=[\\d.]+[^\"]*\"'","handlingStrategy":"validation","validationCode":"# Pre-flight check mirroring the reader (line 44) and writer (line 66) regexes\nimport re\nfrom pathlib import Path\n\nPYPROJECT_FILE = Path(\"pyproject.toml\")\nREAD_RE = re.compile(r'\"anthropic>=([\\d.]+)')\nWRITE_RE = re.compile(r'\"anthropic>=[\\d.]+\"')\n\ndef anthropic_pin_is_rewriteable() -> bool:\n    source = PYPROJECT_FILE.read_text(encoding=\"utf-8\")\n    return bool(READ_RE.search(source)) and bool(WRITE_RE.search(source))\n\n# call before running scripts/update_claude_specs.py\nif not anthropic_pin_is_rewriteable():\n    raise SystemExit(\"pyproject.toml: anthropic floor-version pin missing or not rewriteable — fix before sync\")","typeGuard":null,"tryCatchPattern":"import subprocess, sys\n\ntry:\n    subprocess.run([sys.executable, \"scripts/update_claude_specs.py\"], check=True)\nexcept subprocess.CalledProcessError:\n    # Deterministic failure — inspect the anthropic entry in [project.optional-dependencies].\n    # Do not retry; fix the dependency string or the regexes at lines 44/66 first.\n    sys.exit(\"update_claude_specs failed: check the anthropic>= pin in pyproject.toml (llm-anthropic extra)\")","preventionTips":["Keep the dependency string in pyproject.toml exactly as \"anthropic>=X.Y.Z\" — double quotes, no spaces, no upper bound — inside the llm-anthropic extra; note the constraint in a comment next to the line.","If you need an upper bound (e.g. \"anthropic>=0.120.2,<1\"), update BOTH regexes in scripts/update_claude_specs.py (reader at line 44, writer at line 66) in the same commit — [\\d.]+ followed by a quote cannot match a comma-suffixed specifier.","Run python scripts/update_claude_specs.py locally (or a lint that greps for the pin) before pushing dependency-range changes, so the scheduled CI job does not discover the mismatch.","Remember the error path only triggers when PyPI reports a newer version: a passing run today does not prove the regex still matches, so validate the pin shape explicitly rather than relying on green CI."],"tags":["python","regex","toml","dependencies","ci","maintenance-script"],"backgroundTag":null,"analyzedSha":"4a030776a31a901ad80b1bf4d7faa2c1a367efbb","analyzedAt":"2026-08-15T03:51:26.951Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}