{"record":{"id":"babd3d02135375c6","repo":"ruvnet/RuView","slug":"manifest-is-not-valid-json-e","errorCode":null,"errorMessage":"manifest is not valid JSON: {e}","messagePattern":"manifest is not valid JSON: (.+?)","errorType":"exception","errorClass":"ManifestError","httpStatus":null,"severity":"error","filePath":"scripts/check_fix_markers.py","lineNumber":73,"sourceCode":"DIM = lambda s: _c(\"2\", s)\nBOLD = lambda s: _c(\"1\", s)\n\nOK_MARK = \"PASS\"\nBAD_MARK = \"FAIL\"\nARROW = \"->\"\n\n\nclass ManifestError(Exception):\n    pass\n\n\ndef load_manifest() -> dict:\n    if not MANIFEST_PATH.exists():\n        raise ManifestError(f\"manifest not found: {MANIFEST_PATH}\")\n    try:\n        data = json.loads(MANIFEST_PATH.read_text(encoding=\"utf-8\"))\n    except json.JSONDecodeError as e:\n        raise ManifestError(f\"manifest is not valid JSON: {e}\") from e\n    if not isinstance(data, dict) or not isinstance(data.get(\"markers\"), list):\n        raise ManifestError(\"manifest must be an object with a 'markers' array\")\n    ids = [m.get(\"id\") for m in data[\"markers\"]]\n    dupes = {i for i in ids if ids.count(i) > 1}\n    if dupes:\n        raise ManifestError(f\"duplicate marker ids: {sorted(dupes)}\")\n    return data\n\n\ndef _pattern_found(text: str, pattern: str) -> bool:\n    if len(pattern) >= 2 and pattern.startswith(\"/\") and pattern.endswith(\"/\"):\n        return re.search(pattern[1:-1], text, re.MULTILINE) is not None\n    return pattern in text\n\n\ndef check_marker(marker: dict) -> tuple[bool, list[str]]:\n    \"\"\"Return (ok, problems) for a single marker.\"\"\"\n    problems: list[str] = []","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/ruvnet/RuView/blob/4685618388a5e49fad5b3005806f3bdd6a7c25c3/scripts/check_fix_markers.py#L55-L91","documentation":"load_manifest() in scripts/check_fix_markers.py parses scripts/fix-markers.json with json.loads and re-raises any json.JSONDecodeError as ManifestError with the parser's message (line/column included). The manifest is strict JSON, so anything the stdlib JSON parser rejects — trailing commas, comments, single quotes, unresolved merge-conflict markers — triggers this before any marker checking happens.","triggerScenarios":"Hand-editing fix-markers.json and leaving a trailing comma, adding a // comment, or saving while the file contains <<<<<<< / ======= / >>>>>>> conflict markers after a git merge.","commonSituations":"Merge conflicts resolved incompletely; an editor or code-formatter that does not produce strict JSON; truncated file from an interrupted write or bad git operation.","solutions":["Locate the exact syntax error: `python -m json.tool scripts/fix-markers.json` — it prints line/column","Search for conflict artifacts: `grep -nE '^(<<<<<<<|=======|>>>>>>>)' scripts/fix-markers.json` and resolve them","Remove trailing commas, comments, and single-quoted strings; JSON requires double quotes","Validate in a pre-commit hook or editor JSON linter before committing the manifest"],"exampleFix":"# before (trailing comma after last marker)\n  { \"id\": \"RuView#521\", \"files\": [\"v2/crates/x/src/lib.rs\"] },\n]\n\n# after\n  { \"id\": \"RuView#521\", \"files\": [\"v2/crates/x/src/lib.rs\"] }\n]","handlingStrategy":"validation","validationCode":"import json\nfrom pathlib import Path\n\njson.loads(Path(\"scripts/fix-markers.json\").read_text(encoding=\"utf-8\"))  # raises before the script does, with line/column","typeGuard":null,"tryCatchPattern":"try:\n    data = load_manifest()\nexcept ManifestError as e:\n    print(f\"bad manifest: {e}\")  # exit 2 semantics; fix the file, do not retry","preventionTips":["Validate the manifest with `python -m json.tool` in a pre-commit hook","Never leave merge-conflict markers in JSON files","Use a JSON-aware editor/linter that rejects trailing commas and comments"],"tags":["json","manifest","ci","python","syntax-error","scripts"],"backgroundTag":null,"analyzedSha":"4685618388a5e49fad5b3005806f3bdd6a7c25c3","analyzedAt":"2026-08-16T06:09:40.886Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}