NationalSecurityAgency/ghidra · error · KeyError

Breakpoints[{breaknum}] does not exist

Error message

Breakpoints[{breaknum}] does not exist

What it means

Raised by find_bpt_by_number() (methods.py:183-184) as a KeyError when dbg()._control.GetBreakpointById(breaknum) raises E_NOINTERFACE_Error — the dbgeng COM control has no breakpoint with that numeric ID. The KeyError shape lets callers do dict-style 'does this breakpoint exist?' lookups.

Source

Thrown at Ghidra/Debug/Debugger-agent-dbgeng/src/main/py/src/ghidradbg/methods.py:184

        raise TypeError(f"{object} is not {err_msg}")
    pnum = int(mat['procnum'])
    tnum = int(mat['tnum'])
    level = int(mat['level'])
    find_proc_by_num(pnum)
    find_thread_by_num(tnum)
    return find_frame_by_level(level)


def find_frame_by_obj(object: TraceObject) -> DbgEng._DEBUG_STACK_FRAME:
    return find_frame_by_pattern(FRAME_PATTERN, object, "a StackFrame")


def find_bpt_by_number(breaknum: int) -> DbgEng.IDebugBreakpoint:
    try:
        bp = dbg()._control.GetBreakpointById(breaknum)
        return bp
    except exception.E_NOINTERFACE_Error:
        raise KeyError(f"Breakpoints[{breaknum}] does not exist")


def find_bpt_by_pattern(pattern: re.Pattern, object: TraceObject,
                        err_msg: str) -> DbgEng.IDebugBreakpoint:
    mat = pattern.fullmatch(object.path)
    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) -> DbgEng.IDebugBreakpoint:
    return find_bpt_by_pattern(PROC_BREAKBPT_PATTERN, object, "a BreakpointSpec")


def find_evt_by_number(eventnum: int) -> DbgEng._DEBUG_SPECIFIC_FILTER_PARAMETERS:
    try:
        return util.GetSpecificFilterParameters(eventnum, 1)

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Re-enumerate breakpoints (dbg()._control.EnumerateBreakpoints / the agent's Breakpoints container) to get fresh IDs before lookup.
  2. Wrap the lookup in a try/except KeyError to handle a missing breakpoint gracefully.
  3. Avoid caching breakpoint IDs across process create/restart boundaries.

Example fix

// before
bp = find_bpt_by_number(cached_id)   # KeyError if deleted

// after
try:
    bp = find_bpt_by_number(cached_id)
except KeyError:
    # re-enumerate and use a valid id
    bp = find_bpt_by_number(fresh_id)
Defensive patterns

Strategy: try-catch

Validate before calling

def bpt_exists(breaknum) -> bool:
    try:
        dbg()._control.GetBreakpointById(breaknum)
        return True
    except exception.E_NOINTERFACE_Error:
        return False

if not bpt_exists(breaknum):
    raise KeyError(f'no breakpoint {breaknum}; re-enumerate')

Type guard

def bpt_exists(breaknum) -> bool:
    from ghidradbg.util import dbg
    from ghidradbg import exception
    try:
        dbg()._control.GetBreakpointById(breaknum)
        return True
    except exception.E_NOINTERFACE_Error:
        return False

Try / catch

try:
    bp = find_bpt_by_number(breaknum)
except KeyError as e:
    if 'does not exist' in str(e):
        # re-enumerate and pick a valid id
        raise KeyError(f'stale breakpoint id {breaknum}; refresh from the Breakpoints container') from e
    raise

Prevention

When it happens

Trigger: Looking up a breakpoint ID that was deleted or never created; referencing a breakpoint number after the target process restarted (IDs reset); stale breakpoint number captured from a previous session.

Common situations: Script caching breakpoint IDs across a process restart; UI referencing a breakpoint removed by the user; off-by-one in breakpoint enumeration.

Related errors


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