{"record":{"id":"1c370c4cc91ae551","repo":"python/cpython","slug":"no-history-item-at-position-d","errorCode":null,"errorMessage":"No history item at position %d","messagePattern":"No history item at position (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"Lib/_pyrepl/readline.py","lineNumber":513,"sourceCode":"                f.write(entry + \"\\n\")\n        self.set_history_length(saved_length + length)\n\n    def clear_history(self) -> None:\n        del self.get_reader().history[:]\n\n    def get_history_item(self, index: int) -> str | None:\n        history = self.get_reader().history\n        if 1 <= index <= len(history):\n            return history[index - 1]\n        else:\n            return None  # like readline.c\n\n    def remove_history_item(self, index: int) -> None:\n        history = self.get_reader().history\n        if 0 <= index < len(history):\n            del history[index]\n        else:\n            raise ValueError(\"No history item at position %d\" % index)\n            # like readline.c\n\n    def replace_history_item(self, index: int, line: str) -> None:\n        history = self.get_reader().history\n        if 0 <= index < len(history):\n            history[index] = self._histline(line)\n        else:\n            raise ValueError(\"No history item at position %d\" % index)\n            # like readline.c\n\n    def add_history(self, line: str) -> None:\n        self.get_reader().history.append(self._histline(line))\n\n    def set_startup_hook(self, function: Callback | None = None) -> None:\n        self.startup_hook = function\n\n    def get_line_buffer(self) -> str:\n        return self.get_reader().get_unicode()","sourceCodeStart":495,"sourceCodeEnd":531,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/_pyrepl/readline.py#L495-L531","documentation":"ValueError from remove_history_item in Lib/_pyrepl/readline.py. Unlike get_history_item, which uses readline's 1-based convention, remove_history_item indexes the reader's history list 0-based and requires 0 <= index < len(history). Any out-of-range index raises 'No history item at position %d', mirroring GNU readline's error.","triggerScenarios":"readline.remove_history_item(i) with i >= len(history) or i < 0. Classic trap: iterating with 1-based indices taken from get_current_history_length() or code written against get_history_item's 1-based API.","commonSituations":"Porting readline C or ctypes code that mixes 1-based and 0-based conventions; popping history entries in a loop with a stale length after each removal (length shrinks as you delete); off-by-one when deleting the last item (index == length).","solutions":["Use 0-based indices and clamp to current length: 0 <= i < readline.get_current_history_length().","When deleting multiple items, delete from the end downward (for i in range(n-1, -1, -1)) so indices stay valid, or re-read the length each iteration.","Remember the asymmetry: get_history_item is 1-based, remove/replace are 0-based — same as GNU readline."],"exampleFix":"# before\nfor i in range(1, readline.get_current_history_length()):\n    readline.remove_history_item(i)  # ValueError on later iterations\n\n# after\nfor i in range(readline.get_current_history_length() - 1, -1, -1):\n    readline.remove_history_item(i)","handlingStrategy":"validation","validationCode":"import readline\n\ndef remove_history_safe(i):\n    if 0 <= i < readline.get_current_history_length():\n        readline.remove_history_item(i)\n    else:\n        raise ValueError(f'no history item {i}')","typeGuard":null,"tryCatchPattern":null,"preventionTips":["remove/replace are 0-based; only get_history_item is 1-based.","Delete from the end downward when clearing history.","Re-read length each iteration in mutating loops."],"tags":["python","pyrepl","readline","history","off-by-one"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}