{"record":{"id":"251a70c2aa79914b","repo":"tirth8205/code-review-graph","slug":"refusing-to-remove-the-json-document-root","errorCode":null,"errorMessage":"refusing to remove the JSON document root","messagePattern":"refusing to remove the JSON document root","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"code_review_graph/uninstall.py","lineNumber":336,"sourceCode":"        if isinstance(component, str):\n            member = next(\n                (item for item in _object_members(tokens, current) if item.key == component),\n                None,\n            )\n            if member is None:\n                raise KeyError(component)\n            current = member.value_index\n        else:\n            elements = _array_elements(tokens, current)\n            if component < 0 or component >= len(elements):\n                raise IndexError(component)\n            current = elements[component].value_index\n    return current\n\n\ndef _removal_ranges(tokens: Sequence[_Token], path: Sequence[str | int]) -> list[tuple[int, int]]:\n    if not path:\n        raise ValueError(\"refusing to remove the JSON document root\")\n    parent_index = _find_value(tokens, path[:-1])\n    component = path[-1]\n    if isinstance(component, str):\n        members = _object_members(tokens, parent_index)\n        sibling_index = next(\n            (index for index, member in enumerate(members) if member.key == component),\n            None,\n        )\n        if sibling_index is None:\n            raise KeyError(component)\n        item = members[sibling_index]\n        start = tokens[item.key_index].start\n        end = tokens[item.value_end - 1].end\n        if item.comma_index is not None:\n            return [(start, tokens[item.comma_index].end)]\n        if sibling_index > 0:\n            previous = members[sibling_index - 1]\n            if previous.comma_index is not None:","sourceCodeStart":318,"sourceCodeEnd":354,"githubUrl":"https://github.com/tirth8205/code-review-graph/blob/b58668751ab0c7670c078cf7cbd4d1f5b8e54f81/code_review_graph/uninstall.py#L318-L354","documentation":"Raised by _removal_ranges as a safety check: the requested removal path is empty, which would target the entire JSON document root. Deleting the root is almost never the intent of a surgical key-removal tool, so it refuses with this error instead of wiping the file's whole JSON content.","triggerScenarios":"Calling _remove_jsonc_paths (or a wrapper) with an empty path list/tuple, e.g. paths=[()] or paths=[[]]. Also when path-building code computes zero components because of a config/key naming bug or empty string split (''.split('.') yields []).","commonSituations":"Caller builds paths dynamically from user input and receives an empty key; a default/empty server name is passed during uninstall; refactoring changed a path constant to an empty sequence.","solutions":["Fix the caller to always pass at least one non-empty path component","If whole-file removal is genuinely intended, delete/truncate the file explicitly instead of using the path-removal API","Validate paths before calling: reject empty sequences and components derived from empty strings","Add a unit test asserting the uninstall path list is never empty"],"exampleFix":"# before\n_remove_jsonc_paths(text, paths=[()])  # -> refusing to remove the JSON document root\n\n# after\npaths = [p for p in paths if p]\nif not paths:\n    raise ValueError(\"no removable paths specified\")\n_remove_jsonc_paths(text, paths=paths)","handlingStrategy":"validation","validationCode":"def valid_removal_paths(paths) -> bool:\n    return bool(paths) and all(isinstance(p, (list, tuple)) and len(p) > 0 for p in paths)","typeGuard":"def is_nonempty_path(p) -> bool:\n    return isinstance(p, (list, tuple)) and len(p) > 0 and all(c != '' for c in p)","tryCatchPattern":"try:\n    _remove_jsonc_paths(text, paths)\nexcept ValueError as e:\n    if 'document root' in str(e):\n        raise RuntimeError('bug: empty removal path generated') from e","preventionTips":["Filter empty paths before calling the API","Never derive paths from ''.split() or empty user input without checking","If whole-file removal is intended, delete the file directly instead"],"tags":["json","jsonc","guard-clause","path-validation","uninstall"],"backgroundTag":"invalid-json-path","analyzedSha":"b58668751ab0c7670c078cf7cbd4d1f5b8e54f81","analyzedAt":"2026-08-28T13:19:08.966Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}