NationalSecurityAgency/ghidra · error · KeyError

Watchpoints[{watchnum}] does not exist

Error message

Watchpoints[{watchnum}] does not exist

What it means

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.

Source

Thrown at Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/methods.py:197

    if mat is None:
        raise TypeError(f"{object} is not {err_msg}")
    breaknum = int(mat['breaknum'])
    return find_bpt_by_number(breaknum)


def find_bpt_by_obj(object: TraceObject) -> lldb.SBBreakpoint:
    return find_bpt_by_pattern(PROC_BREAK_PATTERN, object, "a BreakpointSpec")


# Oof. no lldb/Python method to get breakpoint by number
# I could keep my own cache in a dict, but why?
def find_wpt_by_number(watchnum: int) -> lldb.SBWatchpoint:
    # TODO: If len exceeds some threshold, use binary search?
    for i in range(0, util.get_target().GetNumWatchpoints()):
        w = util.get_target().GetWatchpointAtIndex(i)
        if w.GetID() == watchnum:
            return w
    raise KeyError(f"Watchpoints[{watchnum}] does not exist")


def find_wpt_by_pattern(pattern: re.Pattern, object: TraceObject,
                        err_msg: str) -> lldb.SBWatchpoint:
    mat = pattern.fullmatch(object.path)
    if mat is None:
        raise TypeError(f"{object} is not {err_msg}")
    watchnum = int(mat['watchnum'])
    return find_wpt_by_number(watchnum)


def find_wpt_by_obj(object: TraceObject) -> lldb.SBWatchpoint:
    return find_wpt_by_pattern(PROC_WATCH_PATTERN, object, "a WatchpointSpec")


def find_bptlocnum_by_pattern(pattern: re.Pattern, object: TraceObject,
                              err_msg: str) -> Tuple[int, int]:
    mat = pattern.fullmatch(object.path)

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Refresh the trace watchpoint list with 'ghidra trace put-watches' before operating on watchpoint paths.
  2. Check [util.get_target().GetWatchpointAtIndex(i).GetID() for i in range(util.get_target().GetNumWatchpoints())] for the ID.
  3. Catch KeyError and trigger a trace re-sync.

Example fix

# before
wpt = find_wpt_by_number(watchnum)

# after: validate
wids = {
    util.get_target().GetWatchpointAtIndex(i).GetID()
    for i in range(util.get_target().GetNumWatchpoints())
}
if watchnum not in wids:
    raise KeyError(f"Watchpoints[{watchnum}] does not exist")
wpt = find_wpt_by_number(watchnum)
Defensive patterns

Strategy: try-catch

Validate before calling

tgt = util.get_target()
existing = {tgt.GetWatchpointAtIndex(i).GetID() for i in range(tgt.GetNumWatchpoints())}
if watchnum not in existing:
    pass  # do not call find_wpt_by_number

Try / catch

try:
    wpt = find_wpt_by_number(watchnum)
except KeyError:
    refresh_watchpoints()
    wpt = find_wpt_by_number(watchnum)

Prevention

When it happens

Trigger: 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.

Common situations: The watchpoint was deleted or hit-and-removed between trace snapshot and operation. The watchnum is stale or from a prior target session.

Related errors


AI-assisted analysis of NationalSecurityAgency/ghidra@d5f144c24d (2026-08-14). Data as JSON: /api/errors/09866eace74d3bea. Report an issue: GitHub.