NationalSecurityAgency/ghidra · error · ValueError

Object {object} does not support description level

Error message

Object {object} does not support description level

What it means

Raised by get_description when a non-SBWatchpoint object is passed with a non-None level parameter. Only lldb.SBWatchpoint.GetDescription accepts a second 'level' argument; SBThread, SBBreakpoint, and SBEvent ignore the level, so the function guards against unsupported types.

Source

Thrown at Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/util.py:285


def get_eval(expr: str) -> lldb.SBValue:
    eval = get_target().EvaluateExpression(expr)
    if eval.GetError().Fail():
        raise ValueError(eval.GetError().GetCString())
    return eval


def get_description(object: Union[
        lldb.SBThread, lldb.SBBreakpoint, lldb.SBWatchpoint, lldb.SBEvent],
        level: Optional[int] = None) -> str:
    stream = lldb.SBStream()
    if level is None:
        object.GetDescription(stream)
    elif isinstance(object, lldb.SBWatchpoint):
        object.GetDescription(stream, level)
    else:
        raise ValueError(f"Object {object} does not support description level")
    return escape_ansi(stream.GetData())


conv_map: Dict[str, str] = {}


def get_convenience_variable(id: str) -> str:
    # val = get_target().GetEnvironment().Get(id)
    if id not in conv_map:
        return "auto"
    val = conv_map[id]
    if val is None:
        return "auto"
    return val


def set_convenience_variable(id: str, value: str) -> None:
    # env = get_target().GetEnvironment()

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Pass level=None when calling get_description for non-SBWatchpoint objects.
  2. Check isinstance(object, lldb.SBWatchpoint) before passing a level value.
  3. Restructure to call object.GetDescription(stream) for non-watchpoint types and object.GetDescription(stream, level) only for watchpoints.

Example fix

# before: level passed for non-watchpoint
lvl = some_level
desc = get_description(thread_obj, level=lvl)  # SBThread does not support level

# after: only pass level for watchpoints
desc = get_description(
    thread_obj,
    level=lvl if isinstance(thread_obj, lldb.SBWatchpoint) else None,
)
Defensive patterns

Strategy: type-guard

Validate before calling

if level is not None and not isinstance(object, lldb.SBWatchpoint):
    level = None  # or raise early

Type guard

def supports_description_level(obj) -> bool:
    return isinstance(obj, lldb.SBWatchpoint)

Try / catch

try:
    desc = get_description(obj, level=level)
except ValueError as e:
    if 'does not support description level' in str(e):
        desc = get_description(obj, level=None)
    else:
        raise

Prevention

When it happens

Trigger: get_description is called with level set (not None) and the object is an SBThread, SBBreakpoint, or SBEvent (i.e. not an SBWatchpoint). The elif branch falls through to the raise.

Common situations: Caller assumes all LLDB description-capable objects support the level parameter. A watchpoint-specific code path was generalized to other object types without accounting for the API difference.

Related errors


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