NationalSecurityAgency/ghidra · error · IllegalArgumentException
Register edits require a thread.
Error message
Register edits require a thread.
What it means
Thrown as IllegalArgumentException by RW_TRACE.setVariable (ControlMode.java:239-242) when the mapped host address is a register address but coordinates.getThread() is null. Register state lives in a per-thread register space (TraceMemorySpace for the thread/frame), so a register edit is impossible without a thread; memory edits do not require one.
Source
Thrown at Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/control/ControlMode.java:241
@Override
public CompletableFuture<Void> setVariable(PluginTool tool,
DebuggerCoordinates coordinates, Address guestAddress, byte[] data) {
Trace trace = coordinates.getTrace();
TracePlatform platform = platformFor(coordinates, guestAddress);
long snap = coordinates.getViewSnap();
Address hostAddress = platform.mapGuestToHost(guestAddress);
if (hostAddress == null) {
throw new IllegalArgumentException(
"Guest address " + guestAddress + " is not mapped");
}
TraceMemoryOperations memOrRegs;
Address overlayAddress;
try (Transaction tx = trace.openTransaction("Edit Variable")) {
if (hostAddress.isRegisterAddress()) {
TraceThread thread = coordinates.getThread();
if (thread == null) {
throw new IllegalArgumentException("Register edits require a thread.");
}
TraceMemorySpace regs = trace.getMemoryManager()
.getMemoryRegisterSpace(thread, coordinates.getFrame(),
true);
memOrRegs = regs;
overlayAddress = regs.getAddressSpace().getOverlayAddress(hostAddress);
}
else {
memOrRegs = trace.getMemoryManager();
overlayAddress = hostAddress;
}
if (memOrRegs.putBytes(snap, overlayAddress,
ByteBuffer.wrap(data)) != data.length) {
return CompletableFuture.failedFuture(new MemoryAccessException());
}
}
return CompletableFuture.completedFuture(null);
}View on GitHub (pinned to d5f144c24d)
Solutions
- Activate/select a thread in the coordinates before editing a register: coordinates = coordinates.thread(someThread).
- If editing memory, ensure the address is a memory address (not a register address) so the thread precondition is skipped.
- Check coordinates.getThread() != null before issuing a register edit.
Example fix
// before RW_TRACE.setVariable(tool, coordinates, regAddress, data); // no thread // after DebuggerCoordinates withThread = coordinates.thread(selectedThread); RW_TRACE.setVariable(tool, withThread, regAddress, data);
Defensive patterns
Strategy: validation
Validate before calling
if (hostAddress.isRegisterAddress() && coordinates.getThread() == null) {
throw new IllegalStateException("select a thread before editing registers");
} Type guard
boolean canEditRegister(DebuggerCoordinates c, Address host) {
return !host.isRegisterAddress() || c.getThread() != null;
} Try / catch
try {
RW_TRACE.setVariable(tool, coordinates, regAddress, data);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("require a thread")) {
coordinates = coordinates.thread(selectThread());
RW_TRACE.setVariable(tool, coordinates, regAddress, data);
} else throw e;
} Prevention
- Activate a thread before any register edit.
- Use RW_TRACE.isVariableEditable() to pre-check editability.
- Distinguish register vs memory addresses before dispatching.
When it happens
Trigger: Editing a register variable in RW_TRACE mode while no thread is active in DebuggerCoordinates: coordinates.getThread() == null and hostAddress.isRegisterAddress() is true.
Common situations: A user opens the Registers view at the process level without selecting a thread; programmatic edit issued before thread activation; a coordinates object built without a thread for a register write.
Related errors
- Emulator edits require a thread.
- Guest address {guestAddress} is not mapped
- Cannot emulate using a Fixed Program View
- Unsupported value: {}
- Invalid value for {}
AI-assisted analysis of NationalSecurityAgency/ghidra@d5f144c24d (2026-08-14).
Data as JSON: /api/errors/96b3412328f0766c.
Report an issue: GitHub.