NationalSecurityAgency/ghidra · error · ValueError

Cannot convert: {value:r}

Error message

Cannot convert: {value:r}

What it means

Raised by Trace._fix_value() when a value tagged with schema OBJECT is neither a DetachedObject nor a TraceObject. _fix_value() coerces wire objects into TraceObject handles; only those two types are acceptable for an OBJECT schema, so any other type is an internal contract violation. End users normally should not see this — it implies the schema/value pairing is inconsistent.

Source

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

        if isinstance(path_or_id, str):
            fut_id: Future[int] = Future()

            def _done(fut_d_obj: Future[DetachedObject]) -> None:
                fut_id.set_result(fut_d_obj.result().id)

            fut_or_d_obj.add_done_callback(_done)
            return TraceObject(self, fut_id, path_or_id)

    def _fix_value(self, value: Any, schema: sch.Schema) -> Any:
        if schema != sch.OBJECT:
            return value
        elif isinstance(value, DetachedObject):
            return TraceObject(self, value.id, value.path)
        elif isinstance(value, TraceObject):
            return value
        else:
            raise ValueError(f"Cannot convert: {value:r}")

    def _make_values(self, values: Iterable[Tuple[
            DetachedObject, Lifespan, str, Tuple[Any, sch.Schema]
    ]]) -> List[TraceObjectValue]:
        return [
            TraceObjectValue(TraceObject(self, d_obj.id, d_obj.path),
                             span, key, self._fix_value(value, schema), schema)
            for d_obj, span, key, (value, schema) in values
        ]

    def _convert_values(self, results: Union[
        List[Tuple[DetachedObject, Lifespan, str, Tuple[Any, sch.Schema]]],
        Future[List[
            Tuple[DetachedObject, Lifespan, str, Tuple[Any, sch.Schema]]]]]):
        if isinstance(results, List):
            return self._make_values(results)

        fut_values: Future[List[TraceObjectValue]] = Future()

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Ensure OBJECT-typed values are always produced via trace.proxy_object()/from_id/from_path so they are TraceObject instances.
  2. Verify the client and server run compatible ghidratrace versions (see negotiate/version).
  3. If writing custom method glue, double-check the declared schema matches the actual returned type.

Example fix

// before (server-side method returning a raw id for an OBJECT schema)
return raw_id
// after
return trace.proxy_object(id=raw_id)
Defensive patterns

Strategy: type-guard

Validate before calling

from ghidratrace.client import TraceObject, DetachedObject
if schema == sch.OBJECT and not isinstance(value, (TraceObject, DetachedObject)):
    raise TypeError('OBJECT value must be a TraceObject/DetachedObject')

Type guard

def is_object_value(v) -> bool:
    from ghidratrace.client import TraceObject, DetachedObject
    return isinstance(v, (TraceObject, DetachedObject))

Prevention

When it happens

Trigger: An OBJECT-typed method return or element arriving as a raw dict, int, or str; a custom schema or method registration that declared sch.OBJECT but returned an incompatible Python object; mismatched ghidratrace versions between client and server.

Common situations: Version skew between the Python trace client and the Ghidra backend; a plugin that hand-constructs values instead of using proxy_object; corruption/partial deserialization.

Related errors


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