skylot/jadx · error · JadxRuntimeException
Can't remove SSA var: {}, still in use, count: {}, list: {
Error message
Can't remove SSA var: {}, still in use, count: {}, list:
{} What it means
Thrown by InsnRemover.removeSsaVar when an SSA variable cannot be removed because it still has live uses: it has uses outside phi instructions and outside DONT_GENERATE instructions. removeSsaVar only unbinds vars whose remaining uses are all in PHIs or in instructions marked DONT_GENERATE; anything else means removing the var would orphan a live use, so it refuses.
Source
Thrown at jadx-core/src/main/java/jadx/core/utils/InsnRemover.java:161
for (RegisterArg arg : new ArrayList<>(ssaVar.getUseList())) {
InsnNode parentInsn = arg.getParentInsn();
if (parentInsn != null) {
((PhiInsn) parentInsn).removeArg(arg);
}
}
mth.removeSVar(ssaVar);
return;
}
// check if all usage only in not generated instructions
if (allMatch(ssaVar.getUseList(),
arg -> arg.contains(AFlag.DONT_GENERATE) || InsnUtils.contains(arg.getParentInsn(), AFlag.DONT_GENERATE))) {
for (RegisterArg arg : ssaVar.getUseList()) {
arg.resetSSAVar();
}
mth.removeSVar(ssaVar);
return;
}
throw new JadxRuntimeException("Can't remove SSA var: " + ssaVar + ", still in use, count: " + useCount
+ ", list:\n " + ssaVar.getUseList().stream()
.map(arg -> arg + " from " + arg.getParentInsn())
.collect(Collectors.joining("\n ")));
}
public static void unbindArgUsage(@Nullable MethodNode mth, InsnArg arg) {
if (arg instanceof RegisterArg) {
RegisterArg reg = (RegisterArg) arg;
SSAVar sVar = reg.getSVar();
if (sVar != null) {
sVar.removeUse(reg);
}
} else if (arg instanceof InsnWrapArg) {
InsnWrapArg wrap = (InsnWrapArg) arg;
unbindInsn(mth, wrap.getWrapInsn());
}
}
View on GitHub (pinned to e738a26571)
Solutions
- Unbind all uses of the var first: call InsnRemover.unbindInsn on each consumer, or InsnRemover.unbindAllArgs(mth, insn) on the consuming instructions, before removing the defining instruction.
- Use the higher-level InsnRemover.remove(mth, block, insn) / unbindInsn(mth, insn) entry points rather than removeSsaVar directly - they handle consumer unbinding.
- Inspect the use list in the message to find which live instruction still consumes the var and address it.
- Update jadx; if this fires from a built-in pass, report the method.
Example fix
// before - removing the def while uses remain InsnRemover.remove(mth, block, defInsn); // after - unbind consumers first, then remove InsnRemover.unbindAllArgs(mth, consumerInsn); InsnRemover.remove(mth, block, defInsn);
Defensive patterns
Strategy: validation
Validate before calling
// confirm the var is removable before removing
boolean removable = ssaVar.getUseCount() == 0
|| ssaVar.getUseList().stream().allMatch(a ->
isInsnType(a.getParentInsn(), InsnType.PHI)
|| a.contains(AFlag.DONT_GENERATE)
|| InsnUtils.contains(a.getParentInsn(), AFlag.DONT_GENERATE));
if (!removable) {
// unbind consumers first
InsnRemover.unbindAllArgs(mth, consumerInsn);
} Try / catch
try {
InsnRemover.remove(mth, block, insn);
} catch (JadxRuntimeException e) {
if (e.getMessage().startsWith("Can't remove SSA var")) {
LOG.error("var still in use; uses:\n{}", e.getMessage());
} else { throw e; }
} Prevention
- Always unbind consumers (InsnRemover.unbindAllArgs / unbindInsn) before removing a defining instruction.
- Prefer the high-level InsnRemover.remove entry points over removeSsaVar.
- Inspect the use list in the message to find the live consumer that still reads the var.
When it happens
Trigger: Calling InsnRemover.remove on an instruction whose result SSAVar is still used by another live (generated) instruction, or unbinding an SSA var before unbinding its consumers. The message lists every offending use with its parent instruction so you can see who still reads the var.
Common situations: Plugin/pass code that removes a defining instruction without first removing or re-routing its consumers; or an internal jadx pass that ordered removals incorrectly. The fix is almost always to unbind the consumers (InsnRemover.unbindArgUsage / unbindInsn) before removing the var.
Related errors
- Live variable analysis reach iterations limit, blocks count:
- Insn not found in method: {}
- Block not found by insn: {}
- Several immutable types in one variable: {}, vars: {}
- Phi nodes fix limit reached!
AI-assisted analysis of skylot/jadx@e738a26571 (2026-08-14).
Data as JSON: /api/errors/bb1497fb56a6910d.
Report an issue: GitHub.