skylot/jadx · error · JadxRuntimeException

Failed to load config file: {}

Error message

Failed to load config file: {}

What it means

Thrown by SmaliDebugger.handShake when writing the JDWP handshake bytes to the output stream or reading the 14-byte handshake reply from the input stream raises any exception (it catches broad Exception). It means the underlying transport failed during the JDWP handshake exchange itself: the peer reset the connection, the stream hit EOF, or the 5-second socket timeout elapsed before 14 bytes arrived.

Source

Thrown at jadx-cli/src/main/java/jadx/cli/config/JadxConfigAdapter.java:73

	}

	public Path getConfigPath() {
		return configPath;
	}

	public String getDefaultConfigFileName() {
		return defaultConfigFileName;
	}

	public @Nullable T load() {
		if (!Files.isRegularFile(configPath)) {
			// file not found
			return null;
		}
		try (JsonReader reader = gson.newJsonReader(Files.newBufferedReader(configPath))) {
			return gson.fromJson(reader, configCls);
		} catch (Exception e) {
			throw new JadxRuntimeException("Failed to load config file: " + configPath, e);
		}
	}

	public void save(T configObject) {
		try {
			String jsonStr = gson.toJson(configObject, configCls);
			// don't use stream writer here because serialization errors will corrupt config
			Files.writeString(configPath, jsonStr);
		} catch (Exception e) {
			throw new JadxRuntimeException("Failed to save config file: " + configPath, e);
		}
	}

	public String objectToJsonString(T configObject) {
		return gson.toJson(configObject, configCls);
	}

	public T jsonStringToObject(String jsonStr) {

View on GitHub (pinned to e738a26571)

Solutions

  1. Re-establish the ADB jdwp forward for the live process id and retry attach.
  2. Confirm the target app/process is still alive at attach time (not yet crashed or exited).
  3. Make sure only one debugger attaches; jdwp accepts a single handshake client per session.
  4. Inspect the wrapped cause: SocketException/EOFException points to a dead/unreachable peer, SocketTimeoutException to a slow/unresponsive one.
Defensive patterns

Strategy: retry

Validate before calling

// Before attach, confirm a TCP peer is listening and is a jdwp agent:
try (Socket s = new Socket()) {
    s.connect(new InetSocketAddress(host, port), 2000);
    s.setSoTimeout(2000);
    s.getOutputStream().write(JDWP.encodeHandShakePacket());
    byte[] reply = IOUtils.readNBytes(s.getInputStream(), 14);
    if (reply == null || !JDWP.decodeHandShakePacket(reply)) {
        throw new IllegalStateException("No jdwp agent on " + host + ":" + port);
    }
}

Try / catch

// handShake runs inside attach; handle at the attach boundary
try {
    return SmaliDebugger.attach(host, port, listener);
} catch (SmaliDebuggerException e) {
    Throwable cause = e.getCause();
    boolean transientNet = cause instanceof java.net.SocketTimeoutException
        || cause instanceof java.net.SocketException
        || cause instanceof java.io.EOFException;
    if (transientNet) {
        ensureJdwpForward(packageName, port);
        return SmaliDebugger.attach(host, port, listener); // retry once
    }
    throw e;
}

Prevention

When it happens

Trigger: handShake writes JDWP.encodeHandShakePacket() then IOUtils.readNBytes(in, 14). A connection reset, EOF, or socket-read timeout during that exchange is wrapped here. Fires at the very start of initJDWP, before any command packets are sent.

Common situations: The forwarded port has no jdwp listener (immediate reset), the target process died mid-handshake, the device USB link dropped, the socket timeout (5000ms) is too short for a slow device, or another client already consumed the jdwp endpoint.

Related errors


AI-assisted analysis of skylot/jadx@e738a26571 (2026-08-14). Data as JSON: /api/errors/f542993e36937744. Report an issue: GitHub.