{"record":{"id":"52e5fc7b3df9da4c","repo":"NationalSecurityAgency/ghidra","slug":"breakpoints-breaknum-does-not-exist-52e5fc","errorCode":null,"errorMessage":"Breakpoints[{breaknum}] does not exist","messagePattern":"Breakpoints\\[(.+?)\\] does not exist","errorType":"validation","errorClass":"KeyError","httpStatus":null,"severity":"error","filePath":"Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/methods.py","lineNumber":241,"sourceCode":"# Because there's no method to get a register by name....\ndef find_reg_by_name(f: gdb.Frame, name: str) -> Union[gdb.RegisterDescriptor,\n                                                       util.RegisterDesc]:\n    for reg in util.get_register_descs(f.architecture()):\n        # TODO: gdb appears to be case sensitive, but until we encounter a\n        # situation where case matters, we'll be insensitive\n        if reg.name.lower() == name.lower():\n            return reg\n    raise KeyError(f\"No such register: {name}\")\n\n\n# Oof. no gdb/Python method to get breakpoint by number\n# I could keep my own cache in a dict, but why?\ndef find_bpt_by_number(breaknum: int) -> gdb.Breakpoint:\n    # TODO: If len exceeds some threshold, use binary search?\n    for b in gdb.breakpoints():\n        if b.number == breaknum:\n            return b\n    raise KeyError(f\"Breakpoints[{breaknum}] does not exist\")\n\n\ndef find_bpt_by_pattern(pattern: re.Pattern, object: TraceObject,\n                        err_msg: str) -> gdb.Breakpoint:\n    mat = pattern.fullmatch(object.path)\n    if mat is None:\n        raise TypeError(f\"{object} is not {err_msg}\")\n    breaknum = int(mat['breaknum'])\n    return find_bpt_by_number(breaknum)\n\n\ndef find_bpt_by_obj(object: TraceObject) -> gdb.Breakpoint:\n    return find_bpt_by_pattern(BREAKPOINT_PATTERN, object, \"a BreakpointSpec\")\n\n\ndef find_bptlocnum_by_pattern(pattern: re.Pattern, object: TraceObject,\n                              err_msg: str) -> Tuple[int, int]:\n    mat = pattern.fullmatch(object.path)","sourceCodeStart":223,"sourceCodeEnd":259,"githubUrl":"https://github.com/NationalSecurityAgency/ghidra/blob/d5f144c24d6bc53c9cbf4448c6d11143e7696206/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/methods.py#L223-L259","documentation":"Raised by find_bpt_by_number when scanning gdb.breakpoints() finds no breakpoint whose .number equals breaknum. The GDB Python API offers no direct get-breakpoint-by-number, so the agent iterates the list; a deleted, conditional, or never-existent breakpoint number leaves the loop empty. KeyError signals Breakpoints[N] does not exist.","triggerScenarios":"Calling find_bpt_by_number with a number parsed from a stale Breakpoints[N] trace object after the breakpoint was deleted or disabled-and-removed; a number from a different GDB session; a breakpoint whose location count changed. Reached via breakpoint method handlers and find_bpt_by_pattern/find_bpt_by_obj.","commonSituations":"Stale trace breakpoint objects referencing deleted breakpoints (very common after 'delete' commands); race between a breakpoint-removed event and a method handler still holding the old number; re-run of the inferior where breakpoint numbers reset.","solutions":["Refresh gdb.breakpoints() and confirm breaknum is present before lookup.","Catch KeyError in callers and invalidate the corresponding Breakpoints[N] trace object, returning 'not present'.","Ensure breakpoint lifecycle hooks (delete/insert) update the trace so stale numbers cannot be queried.","Validate the parsed breaknum is within the range of current breakpoint numbers before delegating."],"exampleFix":"// before\nb = find_bpt_by_number(breaknum)\n// after\nnums = {b.number for b in gdb.breakpoints()}\nif breaknum not in nums:\n    return None  # breakpoint gone; invalidate trace node\nb = find_bpt_by_number(breaknum)","handlingStrategy":"validation","validationCode":"nums = {b.number for b in gdb.breakpoints()}\nif breaknum not in nums:\n    return None  # breakpoint gone\nb = find_bpt_by_number(breaknum)","typeGuard":"def breakpoint_exists(breaknum: int) -> bool:\n    return any(b.number == breaknum for b in gdb.breakpoints())","tryCatchPattern":"try:\n    b = find_bpt_by_number(breaknum)\nexcept KeyError:\n    invalidate_bpt_obj(breaknum)\n    return None","preventionTips":["re-query gdb.breakpoints() before lookup","wire breakpoint-delete hooks to remove stale trace nodes","treat breakpoint numbers as ephemeral across runs"],"tags":["ghidra","gdb","trace-rpc","key-error","breakpoints"],"backgroundTag":null,"analyzedSha":"d5f144c24d6bc53c9cbf4448c6d11143e7696206","analyzedAt":"2026-08-14T01:00:57.564Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}