{"record":{"id":"96b3412328f0766c","repo":"NationalSecurityAgency/ghidra","slug":"register-edits-require-a-thread","errorCode":null,"errorMessage":"Register edits require a thread.","messagePattern":"Register edits require a thread\\.","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/control/ControlMode.java","lineNumber":241,"sourceCode":"\n\t\t@Override\n\t\tpublic CompletableFuture<Void> setVariable(PluginTool tool,\n\t\t\t\tDebuggerCoordinates coordinates, Address guestAddress, byte[] data) {\n\t\t\tTrace trace = coordinates.getTrace();\n\t\t\tTracePlatform platform = platformFor(coordinates, guestAddress);\n\t\t\tlong snap = coordinates.getViewSnap();\n\t\t\tAddress hostAddress = platform.mapGuestToHost(guestAddress);\n\t\t\tif (hostAddress == null) {\n\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Guest address \" + guestAddress + \" is not mapped\");\n\t\t\t}\n\t\t\tTraceMemoryOperations memOrRegs;\n\t\t\tAddress overlayAddress;\n\t\t\ttry (Transaction tx = trace.openTransaction(\"Edit Variable\")) {\n\t\t\t\tif (hostAddress.isRegisterAddress()) {\n\t\t\t\t\tTraceThread thread = coordinates.getThread();\n\t\t\t\t\tif (thread == null) {\n\t\t\t\t\t\tthrow new IllegalArgumentException(\"Register edits require a thread.\");\n\t\t\t\t\t}\n\t\t\t\t\tTraceMemorySpace regs = trace.getMemoryManager()\n\t\t\t\t\t\t\t.getMemoryRegisterSpace(thread, coordinates.getFrame(),\n\t\t\t\t\t\t\t\ttrue);\n\t\t\t\t\tmemOrRegs = regs;\n\t\t\t\t\toverlayAddress = regs.getAddressSpace().getOverlayAddress(hostAddress);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tmemOrRegs = trace.getMemoryManager();\n\t\t\t\t\toverlayAddress = hostAddress;\n\t\t\t\t}\n\t\t\t\tif (memOrRegs.putBytes(snap, overlayAddress,\n\t\t\t\t\tByteBuffer.wrap(data)) != data.length) {\n\t\t\t\t\treturn CompletableFuture.failedFuture(new MemoryAccessException());\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn CompletableFuture.completedFuture(null);\n\t\t}","sourceCodeStart":223,"sourceCodeEnd":259,"githubUrl":"https://github.com/NationalSecurityAgency/ghidra/blob/d5f144c24d6bc53c9cbf4448c6d11143e7696206/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/control/ControlMode.java#L223-L259","documentation":"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.","triggerScenarios":"Editing a register variable in RW_TRACE mode while no thread is active in DebuggerCoordinates: coordinates.getThread() == null and hostAddress.isRegisterAddress() is true.","commonSituations":"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.","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."],"exampleFix":"// before\nRW_TRACE.setVariable(tool, coordinates, regAddress, data);  // no thread\n\n// after\nDebuggerCoordinates withThread = coordinates.thread(selectedThread);\nRW_TRACE.setVariable(tool, withThread, regAddress, data);","handlingStrategy":"validation","validationCode":"if (hostAddress.isRegisterAddress() && coordinates.getThread() == null) {\n    throw new IllegalStateException(\"select a thread before editing registers\");\n}","typeGuard":"boolean canEditRegister(DebuggerCoordinates c, Address host) {\n    return !host.isRegisterAddress() || c.getThread() != null;\n}","tryCatchPattern":"try {\n    RW_TRACE.setVariable(tool, coordinates, regAddress, data);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"require a thread\")) {\n        coordinates = coordinates.thread(selectThread());\n        RW_TRACE.setVariable(tool, coordinates, regAddress, data);\n    } else throw e;\n}","preventionTips":["Activate a thread before any register edit.","Use RW_TRACE.isVariableEditable() to pre-check editability.","Distinguish register vs memory addresses before dispatching."],"tags":["control-mode","register-edit","thread-required","trace","java"],"backgroundTag":null,"analyzedSha":"d5f144c24d6bc53c9cbf4448c6d11143e7696206","analyzedAt":"2026-08-14T01:00:57.564Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}