NationalSecurityAgency/ghidra · error · TypeError
Unions not allowed except with None (for Optional)
Error message
Unions not allowed except with None (for Optional)
What it means
Raised as TypeError by unopt_type() when a method annotation is a typing.Union that contains more than one non-None member. The RMI method-registration machinery only allows Optional[T] (i.e. Union[T, None]) so each parameter maps to exactly one wire schema; a genuine multi-type Union like Union[int,str] is ambiguous and rejected at registration time.
Source
Thrown at Ghidra/Debug/Debugger-rmi-trace/src/main/py/src/ghidratrace/client.py:594
action: str
display: Optional[str]
icon: Optional[str]
ok_text: Optional[str]
description: Optional[str]
parameters: List[RemoteParameter]
return_schema: sch.Schema
callback: Callable
C = TypeVar('C', bound=Callable)
def unopt_type(t: type) -> type:
if not get_origin(t) is Union:
return t
sub = [a for a in get_args(t) if a is not type(None)]
if len(sub) != 1:
raise TypeError("Unions not allowed except with None (for Optional)")
return unopt_type(sub[0])
def find_metadata(annotation: Any, cls: type[T]) -> Tuple[Any, Optional[T]]:
if not hasattr(annotation, '__metadata__'):
return unopt_type(annotation), None
for m in annotation.__metadata__:
if isinstance(m, cls):
return unopt_type(annotation.__origin__), m
return unopt_type(annotation.__origin__), None
class MethodRegistry(object):
def __init__(self, executor: Executor) -> None:
self._methods: Dict[str, RemoteMethod] = {}
self._executor: Executor = executor
View on GitHub (pinned to d5f144c24d)
Solutions
- Narrow the annotation to a single type (e.g. accept int and resolve paths separately), or use Optional[T].
- Use a dedicated type like TraceObject/Union[int,str]-free wrapper that the schema layer can map (see _to_schema's supported types).
- Re-register the method after correcting the annotation; the error is raised once at registration.
Example fix
# before @method def thing(self, ref: Union[int, str]) -> None: ... # after @method def thing(self, ref: int) -> None: ... # or use Optional[int]
Defensive patterns
Strategy: validation
Validate before calling
from typing import get_args, get_origin, Union
if get_origin(t) is Union:
non_none = [a for a in get_args(t) if a is not type(None)]
if len(non_none) != 1:
raise TypeError(f'Union with multiple non-None types not allowed: {t}') Type guard
def is_optional_only(t) -> bool:
from typing import get_args, get_origin, Union
if get_origin(t) is not Union:
return True
return len([a for a in get_args(t) if a is not type(None)]) == 1 Prevention
- Use Optional[T] or a single concrete type for registered method params.
- Run a smoke registration test for new methods.
When it happens
Trigger: Decorating/registering a method whose signature uses Union[int, str] or Union[A, B] (with two real types). The error surfaces when the method registry inspects annotations, not at call time.
Common situations: Writing a custom TraceRmi method that accepts 'either an id or a path' and annotating it as Union[int,str] instead of using a single normalized type; copying a general-purpose helper signature into a registered method.
Related errors
- Cannot get schema for {annotation}
- No saved offers to launch {}
- Range [%s:%x+%x] entirely exceeds space min
- Trace is not from this connection
- CreateTrace: path (name) cannot be empty
AI-assisted analysis of NationalSecurityAgency/ghidra@d5f144c24d (2026-08-14).
Data as JSON: /api/errors/a4f02bff76e52781.
Report an issue: GitHub.