termux/termux-app · error · IOException
[dynamic] JniResult.getErrorString() — composed at runtime:
Error message
[dynamic] JniResult.getErrorString() — composed at runtime: "Retval: {retval}\nErrno: {errno}\nErrmsg: {errmsg}" What it means
Thrown by LocalServerSocket.close() when the JNI closeSocket call returns null or a JniResult with retval != 0. The message is composed at runtime by JniResult.getErrorString() as 'Retval: {retval}\nErrno: {errno}\nErrmsg: {errmsg}'. This signals the native close of the server socket fd failed (e.g. EBADF because the fd was already closed, or a JNI-level fault).
Source
Thrown at termux-shared/src/main/java/com/termux/shared/net/socket/local/LocalServerSocket.java:168
if (logErrorMessage)
Logger.logErrorExtended(LOG_TAG, error.getErrorLogString());
return error;
}
return null;
}
/** Implementation for {@link Closeable#close()} to close server socket. */
@Override
public synchronized void close() throws IOException {
Logger.logDebug(LOG_TAG, "close");
int fd = mLocalSocketRunConfig.getFD();
if (fd >= 0) {
JniResult result = LocalSocketManager.closeSocket(mLocalSocketRunConfig.getLogTitle() + " (server)", fd);
if (result == null || result.retval != 0) {
throw new IOException(JniResult.getErrorString(result));
}
// Update fd to signify that server socket has been closed
mLocalSocketRunConfig.setFD(-1);
}
}
/**
* Delete server socket file if not an abstract namespace socket. This will cause any existing
* running server to stop.
*/
private Error deleteServerSocketFile() {
if (!mLocalSocketRunConfig.isAbstractNamespaceSocket())
return FileUtils.deleteSocketFile(mLocalSocketRunConfig.getTitle() + " server socket file", mLocalSocketRunConfig.getPath(), true);
else
return null;
}
/** Listen and accept new {@link LocalClientSocket}. */View on GitHub (pinned to 3df69d1da1)
Solutions
- Make close() idempotent: skip the JNI call when fd < 0 (it is already set to -1 after a successful close).
- Ensure close() is called exactly once (use try-with-resources on the LocalServerSocket).
- Inspect the JniResult errno/errmsg in the IOException message to identify EBADF vs. native fault.
- If the native library failed to load, load it before any socket operation.
Example fix
// before
if (fd >= 0) {
JniResult result = LocalSocketManager.closeSocket(mLocalSocketRunConfig.getLogTitle() + " (server)", fd);
if (result == null || result.retval != 0) {
throw new IOException(JniResult.getErrorString(result));
}
mLocalSocketRunConfig.setFD(-1);
}
// after (idempotent + log instead of throwing on already-closed fd)
if (fd >= 0) {
JniResult result = LocalSocketManager.closeSocket(mLocalSocketRunConfig.getLogTitle() + " (server)", fd);
mLocalSocketRunConfig.setFD(-1);
if (result == null || result.retval != 0) {
Logger.logWarn(LOG_TAG, "closeSocket returned error on fd " + fd + ": " + JniResult.getErrorString(result));
}
} Defensive patterns
Strategy: try-catch
Validate before calling
if (mLocalSocketRunConfig.getFD() < 0) {
// already closed; nothing to do
return;
} Try / catch
try {
serverSocket.close();
} catch (IOException e) {
// log only; close failures on an already-tearing-down socket are usually benign
Logger.logWarn(LOG_TAG, "close() failed: " + e.getMessage());
} Prevention
- Make close() idempotent by skipping the JNI call when fd < 0.
- Use try-with-resources so close() is invoked exactly once.
- Load the native library before any socket lifecycle operation.
When it happens
Trigger: close() called twice (fd already closed, second close yields EBADF); the fd was invalidated elsewhere; JNI closeSocket raised an exception/returned a non-zero retval; the fd value is stale after the server was stopped.
Common situations: Double-close in a finally block; server socket already torn down by the manager; fd overwritten/corrupted; native lib not loaded so JNI returns an error result.
Related errors
- [dynamic] Error.getErrorMarkdownString() — composed at runti
- Malformed symlink line:
- No SYMLINKS.txt encountered
- Moving termux prefix staging to prefix directory failed
- Invalid path:
AI-assisted analysis of termux/termux-app@3df69d1da1 (2026-08-13).
Data as JSON: /api/errors/576e653a15cd9258.
Report an issue: GitHub.