NationalSecurityAgency/ghidra · error · KeyError
Inferiors[{thread.inferior.num}].Threads[{thread.num}].Stack
Error message
Inferiors[{thread.inferior.num}].Threads[{thread.num}].Stack[{level}] does not exist What it means
Raised in find_frame_by_level while walking the stack upward via frame.older(): the older frame is None, meaning the requested level is deeper than the oldest (root) frame on the current thread's stack. Because GDB's Python API exposes no direct 'get frame by level', the agent walks frame.older() decrementing a counter; hitting None before the counter reaches zero means the level is out of range. KeyError signals a missing indexed entity (Stack[level]).
Source
Thrown at Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/methods.py:189
def find_thread_by_stack_obj(object: TraceObject) -> gdb.InferiorThread:
return find_thread_by_pattern(STACK_PATTERN, object, "a Stack")
def find_frame_by_level(thread: gdb.InferiorThread,
level: int) -> Optional[gdb.Frame]:
# Because threads don't have any attribute to get at frames
thread.switch()
f = util.selected_frame()
if f is None:
return None
# Navigate up or down, because I can't just get by level
down = level - util.get_level(f)
while down > 0:
f = f.older()
if f is None:
raise KeyError(
f"Inferiors[{thread.inferior.num}].Threads[{thread.num}].Stack[{level}] does not exist")
down -= 1
while down < 0:
f = f.newer()
if f is None:
raise KeyError(
f"Inferiors[{thread.inferior.num}].Threads[{thread.num}].Stack[{level}] does not exist")
down += 1
return f
def find_frame_by_pattern(pattern: re.Pattern, object: TraceObject,
err_msg: str) -> Optional[gdb.Frame]:
mat = pattern.fullmatch(object.path)
if mat is None:
raise TypeError(f"{object} is not {err_msg}")
infnum = int(mat['infnum'])
tnum = int(mat['tnum'])View on GitHub (pinned to d5f144c24d)
Solutions
- Bound the requested level to the current frame count (use gdb.execute('bt') length or count via repeated older() before the lookup).
- Refresh the Stack subtree of the trace on each stop before method handlers index frames.
- In callers, treat the KeyError as 'frame not present' and return None / a not-present result rather than propagating.
- Check GDB's backtrace limit (set backtrace limit) is not artificially truncating the walk.
Example fix
// before
f = find_frame_by_level(t, level)
// after
depth = 0
_probe = util.selected_frame()
while _probe is not None:
depth += 1
_probe = _probe.older()
if level >= depth:
return None # level out of range
f = find_frame_by_level(t, level) Defensive patterns
Strategy: validation
Validate before calling
depth = 0
_probe = util.selected_frame()
while _probe is not None:
depth += 1
_probe = _probe.older()
if level >= depth:
return None
f = find_frame_by_level(t, level) Type guard
def frame_level_exists(t: gdb.InferiorThread, level: int) -> bool:
# walk once to test
cur = util.selected_frame()
if cur is None:
return False
delta = level - util.get_level(cur)
cur_level = cur
for _ in range(abs(delta)):
cur_level = cur_level.older() if delta > 0 else cur_level.newer()
if cur_level is None:
return False
return True Try / catch
try:
f = find_frame_by_level(t, level)
except KeyError:
# requested level deeper than stack; refresh Stack subtree
return None Prevention
- recompute stack depth on each stop before indexing frames
- refresh the Stack subtree of the trace before method dispatch
- check GDB backtrace limit is not truncating the walk
When it happens
Trigger: Calling find_frame_by_level with a level larger than the deepest frame index that exists on the thread. Reached when a trace-rpc handler requests a frame by an absolute level parsed from a FRAME_PATTERN object path while the thread's actual stack is shallower (e.g. after a stack unwind truncated frames, or the level was computed from a previous deeper stack).
Common situations: Stale Stack[level] trace objects left after the stack unwound/shrank; backtrace limits (GDB backtrace depth settings) hiding deeper frames; thread resumed and re-stopped with a shallower stack; cross-architecture stub frames that terminate the chain early.
Related errors
- Inferiors[{inf.num}].Threads[{tnum}] does not exist
- No such register: {name}
- Breakpoints[{breaknum}] does not exist
- {object} is not a Module
- Unsupported value: {}
AI-assisted analysis of NationalSecurityAgency/ghidra@d5f144c24d (2026-08-14).
Data as JSON: /api/errors/2d6154f7c4afc0f3.
Report an issue: GitHub.