ruvnet/RuView · error · ManifestError
duplicate marker ids: {sorted(dupes)}
Error message
duplicate marker ids: {sorted(dupes)} What it means
load_manifest() in scripts/check_fix_markers.py collects the 'id' of every entry in the markers array and raises ManifestError listing any id that appears more than once (sorted). Ids are the stable keys used by --only filtering and CI reporting, so duplicates would make marker selection and regression attribution ambiguous.
Source
Thrown at scripts/check_fix_markers.py:79
class ManifestError(Exception):
pass
def load_manifest() -> dict:
if not MANIFEST_PATH.exists():
raise ManifestError(f"manifest not found: {MANIFEST_PATH}")
try:
data = json.loads(MANIFEST_PATH.read_text(encoding="utf-8"))
except json.JSONDecodeError as e:
raise ManifestError(f"manifest is not valid JSON: {e}") from e
if not isinstance(data, dict) or not isinstance(data.get("markers"), list):
raise ManifestError("manifest must be an object with a 'markers' array")
ids = [m.get("id") for m in data["markers"]]
dupes = {i for i in ids if ids.count(i) > 1}
if dupes:
raise ManifestError(f"duplicate marker ids: {sorted(dupes)}")
return data
def _pattern_found(text: str, pattern: str) -> bool:
if len(pattern) >= 2 and pattern.startswith("/") and pattern.endswith("/"):
return re.search(pattern[1:-1], text, re.MULTILINE) is not None
return pattern in text
def check_marker(marker: dict) -> tuple[bool, list[str]]:
"""Return (ok, problems) for a single marker."""
problems: list[str] = []
files = marker.get("files", [])
require = marker.get("require", [])
forbid = marker.get("forbid", [])
if not files:
problems.append("marker lists no files")View on GitHub (pinned to 4685618388)
Solutions
- Grep the manifest for the duplicated id from the error message and rename or delete one entry
- Use issue-qualified unique ids (e.g. "RuView#396") as the existing entries do, so merges rarely collide
- If both entries are legitimate, merge their files/require/forbid lists into one marker with that id
- Re-run `python scripts/check_fix_markers.py` — exit code 0/1 means the manifest now parses
Example fix
# before (two markers share id)
{ "id": "RuView#396", "files": ["a.py"] },
{ "id": "RuView#396", "files": ["b.py"] }
# after
{ "id": "RuView#396", "files": ["a.py", "b.py"] },
{ "id": "RuView#531", "files": ["b.py"] } Defensive patterns
Strategy: validation
Validate before calling
import json
from collections import Counter
from pathlib import Path
def manifest_ids_unique(path: str) -> bool:
data = json.loads(Path(path).read_text(encoding="utf-8"))
ids = [m.get("id") for m in data["markers"]]
return not [i for i, n in Counter(ids).items() if n > 1] Prevention
- Derive marker ids from issue numbers (RuView#NNN) so concurrent branches rarely collide
- Grep for an id before adding a marker with it: grep '"id": "RuView#396"' scripts/fix-markers.json
- Merge duplicate entries' file lists instead of keeping two markers with one id
When it happens
Trigger: Copy-pasting an existing marker block to add a new fix and forgetting to change its id; a git merge that brings the same marker in on both branches; renaming a marker's id in one place while an old entry with that id survives elsewhere in the file.
Common situations: Parallel branches each adding a marker with the same auto-generated id; a bot or script appending markers without checking for existing ids; cherry-picks that duplicate entries.
Related errors
- manifest is not valid JSON: {e}
- manifest not found: {MANIFEST_PATH}
- manifest must be an object with a 'markers' array
- Calibration bundle {path} missing key {key!r}
- Non-JSON response from {method} {path} (status {resp.status}
AI-assisted analysis of ruvnet/RuView@4685618388 (2026-08-16).
Data as JSON: /api/errors/5ecb592545b9a0e4.
Report an issue: GitHub.