NationalSecurityAgency/ghidra · error · ValueError

Object/proxy has neither id nor path!: {}

Error message

Object/proxy has neither id nor path!: {}

What it means

Raised by Client._write_obj_desc() when serializing an object reference and none of the recognized cases matched: the value is not an int, str, DetachedObject, a TraceObject with an int id, a resolved RemoteResult id, or a TraceObject with a str path. Essentially the object carries neither a usable id nor a usable path, so it cannot be written to the wire.

Source

Thrown at Ghidra/Debug/Debugger-rmi-trace/src/main/py/src/ghidratrace/client.py:781

        return Lifespan(msg.min, msg.max)

    @staticmethod
    def _write_obj_spec(to: bufs.ObjSpec, obj: Union[
            str, int, DetachedObject, TraceObject]) -> None:
        if isinstance(obj, int):
            to.id = obj
        elif isinstance(obj, str):
            to.path.path = obj
        elif isinstance(obj, DetachedObject):
            to.id = obj.id
        elif isinstance(obj.id, int):
            to.id = obj.id
        elif isinstance(obj.id, RemoteResult) and obj.id.done():
            to.id = obj.id.result()
        elif isinstance(obj.path, str):
            to.path.path = obj.path
        else:
            raise ValueError(
                "Object/proxy has neither id nor path!: {}".format(obj))

    @staticmethod
    def _read_obj_desc(msg: bufs.ObjDesc) -> DetachedObject:
        return DetachedObject(msg.id, msg.path.path)

    @staticmethod
    def _write_value(to: bufs.Value, value: Any,
                     schema: Optional[sch.Schema] = None) -> None:
        if value is None:
            to.null_value.SetInParent()
            return
        elif isinstance(value, bool):
            to.bool_value = value
            return
        elif isinstance(value, int):
            if schema == sch.BYTE:
                to.byte_value = value

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Ensure the object has at least one concrete identifier (resolved id or path) before it is sent.
  2. Resolve any pending Future id first (call .result()) or use from_path() to guarantee a path.
  3. Construct objects only via the factory helpers (proxy_object/from_id/from_path) rather than directly.

Example fix

// before
obj = trace.proxy_object(id=fut)  # fut is an unresolved Future
// after
obj = trace.proxy_object(id=fut.result())  # resolve first, or pass a path
Defensive patterns

Strategy: validation

Validate before calling

def obj_has_ref(o) -> bool:
    return (getattr(o, 'id', None) is not None and not isinstance(o.id, type(None))) \
        or getattr(getattr(o, 'path', None), 'path', None)
if not obj_has_ref(obj):
    raise ValueError('object has neither resolved id nor path')

Type guard

def is_writable_obj_ref(o) -> bool:
    import concurrent.futures as cf
    oid = getattr(o, 'id', None)
    if isinstance(oid, int): return True
    if isinstance(oid, cf.Future) and oid.done(): return True
    if isinstance(getattr(o, 'path', None), str) or getattr(getattr(o,'path',None),'path',None): return True
    return False

Prevention

When it happens

Trigger: A TraceObject whose id is an unresolved Future and whose path is None; a DetachedObject constructed with non-int id and non-str path; passing an arbitrary object where an object reference was expected.

Common situations: Using a TraceObject before its id/path has been resolved (e.g. a Future that has not completed), version skew, or hand-built mock objects in tests.

Related errors


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