{"record":{"id":"09866eace74d3bea","repo":"NationalSecurityAgency/ghidra","slug":"watchpoints-watchnum-does-not-exist","errorCode":null,"errorMessage":"Watchpoints[{watchnum}] does not exist","messagePattern":"Watchpoints\\[(.+?)\\] does not exist","errorType":"exception","errorClass":"KeyError","httpStatus":null,"severity":"error","filePath":"Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/methods.py","lineNumber":197,"sourceCode":"    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) -> lldb.SBBreakpoint:\n    return find_bpt_by_pattern(PROC_BREAK_PATTERN, object, \"a BreakpointSpec\")\n\n\n# Oof. no lldb/Python method to get breakpoint by number\n# I could keep my own cache in a dict, but why?\ndef find_wpt_by_number(watchnum: int) -> lldb.SBWatchpoint:\n    # TODO: If len exceeds some threshold, use binary search?\n    for i in range(0, util.get_target().GetNumWatchpoints()):\n        w = util.get_target().GetWatchpointAtIndex(i)\n        if w.GetID() == watchnum:\n            return w\n    raise KeyError(f\"Watchpoints[{watchnum}] does not exist\")\n\n\ndef find_wpt_by_pattern(pattern: re.Pattern, object: TraceObject,\n                        err_msg: str) -> lldb.SBWatchpoint:\n    mat = pattern.fullmatch(object.path)\n    if mat is None:\n        raise TypeError(f\"{object} is not {err_msg}\")\n    watchnum = int(mat['watchnum'])\n    return find_wpt_by_number(watchnum)\n\n\ndef find_wpt_by_obj(object: TraceObject) -> lldb.SBWatchpoint:\n    return find_wpt_by_pattern(PROC_WATCH_PATTERN, object, \"a WatchpointSpec\")\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":179,"sourceCodeEnd":215,"githubUrl":"https://github.com/NationalSecurityAgency/ghidra/blob/d5f144c24d6bc53c9cbf4448c6d11143e7696206/Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/methods.py#L179-L215","documentation":"Raised by find_wpt_by_number when iterating the target's watchpoint list (util.get_target().GetWatchpointAtIndex) finds no SBWatchpoint whose GetID() equals the requested watchnum. Like breakpoints, LLDB has no direct watchpoint-by-ID lookup, so the agent scans the list linearly.","triggerScenarios":"find_wpt_by_pattern extracts watchnum from the Watchpoints[<watchnum>] path segment and calls find_wpt_by_number. The for-loop over GetNumWatchpoints() does not find a matching watchpoint.","commonSituations":"The watchpoint was deleted or hit-and-removed between trace snapshot and operation. The watchnum is stale or from a prior target session.","solutions":["Refresh the trace watchpoint list with 'ghidra trace put-watches' before operating on watchpoint paths.","Check [util.get_target().GetWatchpointAtIndex(i).GetID() for i in range(util.get_target().GetNumWatchpoints())] for the ID.","Catch KeyError and trigger a trace re-sync."],"exampleFix":"# before\nwpt = find_wpt_by_number(watchnum)\n\n# after: validate\nwids = {\n    util.get_target().GetWatchpointAtIndex(i).GetID()\n    for i in range(util.get_target().GetNumWatchpoints())\n}\nif watchnum not in wids:\n    raise KeyError(f\"Watchpoints[{watchnum}] does not exist\")\nwpt = find_wpt_by_number(watchnum)","handlingStrategy":"try-catch","validationCode":"tgt = util.get_target()\nexisting = {tgt.GetWatchpointAtIndex(i).GetID() for i in range(tgt.GetNumWatchpoints())}\nif watchnum not in existing:\n    pass  # do not call find_wpt_by_number","typeGuard":null,"tryCatchPattern":"try:\n    wpt = find_wpt_by_number(watchnum)\nexcept KeyError:\n    refresh_watchpoints()\n    wpt = find_wpt_by_number(watchnum)","preventionTips":["Refresh the trace watchpoint list after watchpoint changes.","Verify watchpoint IDs exist in the target before operating on watchpoint paths.","Catch KeyError and trigger a trace re-sync."],"tags":["lldb","debugger-agent","watchpoint-not-found","keyerror","trace-object"],"backgroundTag":null,"analyzedSha":"d5f144c24d6bc53c9cbf4448c6d11143e7696206","analyzedAt":"2026-08-14T01:00:57.564Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}