termux/termux-app · critical · RuntimeException
No SYMLINKS.txt encountered
Error message
No SYMLINKS.txt encountered
What it means
Thrown after the entire bootstrap zip has been iterated if the symlinks list is still empty, meaning no zip entry named exactly 'SYMLINKS.txt' was encountered. This is a fatal integrity check: without the symlink map the prefix is unusable, so setup aborts instead of installing a broken prefix.
Source
Thrown at app/src/main/java/com/termux/app/TermuxInstaller.java:208
if (!isDirectory) {
try (FileOutputStream outStream = new FileOutputStream(targetFile)) {
int readBytes;
while ((readBytes = zipInput.read(buffer)) != -1)
outStream.write(buffer, 0, readBytes);
}
if (zipEntryName.startsWith("bin/") || zipEntryName.startsWith("libexec") ||
zipEntryName.startsWith("lib/apt/apt-helper") || zipEntryName.startsWith("lib/apt/methods")) {
//noinspection OctalInteger
Os.chmod(targetFile.getAbsolutePath(), 0700);
}
}
}
}
}
if (symlinks.isEmpty())
throw new RuntimeException("No SYMLINKS.txt encountered");
for (Pair<String, String> symlink : symlinks) {
Os.symlink(symlink.first, symlink.second);
}
Logger.logInfo(LOG_TAG, "Moving termux prefix staging to prefix directory.");
if (!TERMUX_STAGING_PREFIX_DIR.renameTo(TERMUX_PREFIX_DIR)) {
throw new RuntimeException("Moving termux prefix staging to prefix directory failed");
}
Logger.logInfo(LOG_TAG, "Bootstrap packages installed successfully.");
// Recreate env file since termux prefix was wiped earlier
TermuxShellEnvironment.writeEnvironmentToFile(activity);
activity.runOnUiThread(whenDone);
} catch (final Exception e) {View on GitHub (pinned to 3df69d1da1)
Solutions
- Verify the bundled/downloaded bootstrap zip actually contains a top-level entry named exactly 'SYMLINKS.txt' (case-sensitive, no directory prefix).
- Re-download or regenerate the bootstrap zip from the official termux-packages build for the target arch.
- Check that loadZipBytes() returns the complete, untruncated zip (validate size/checksum before parsing).
- If the manifest legitimately lives elsewhere, update the entry-name comparison to match the real location.
Example fix
// before
if (symlinks.isEmpty())
throw new RuntimeException("No SYMLINKS.txt encountered");
// after (diagnostic: list entry names seen so the cause is obvious)
if (symlinks.isEmpty())
throw new RuntimeException("No SYMLINKS.txt encountered in bootstrap zip. Entries seen: " + seenEntryNames); Defensive patterns
Strategy: validation
Validate before calling
// Before parsing, verify the zip contains SYMLINKS.txt
boolean hasManifest = false;
try (ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(zipBytes))) {
ZipEntry e;
while ((e = zis.getNextEntry()) != null) {
if ("SYMLINKS.txt".equals(e.getName())) { hasManifest = true; break; }
}
}
if (!hasManifest) {
throw new IOException("Bootstrap zip missing SYMLINKS.txt manifest");
} Prevention
- Validate the bootstrap zip checksum before parsing so truncation is caught early.
- Log every zip entry name during development to confirm the manifest is present.
- Pin bootstrap artifacts to a specific released version rather than a moving URL.
When it happens
Trigger: The bootstrap zip is missing the SYMLINKS.txt entry; the entry exists under a different path/name (e.g. nested in a subfolder or with different casing); the zip is truncated so iteration ends before reaching that entry; a wrong/older bootstrap artifact was bundled.
Common situations: Building a custom bootstrap that omits SYMLINKS.txt; corrupt or partial download of the bootstrap zip; using a bootstrap intended for a different package variant; CI caching a stale zip without the manifest.
Related errors
- Malformed symlink line:
- Moving termux prefix staging to prefix directory failed
- Unsupported TERMUX_APP_PACKAGE_VARIANT "%s"
- Unsupported TERMUX_APP_PACKAGE_MANAGER "%s" with variant "%s
- Wrong checksum for %s: expected: %s, actual: %s
AI-assisted analysis of termux/termux-app@3df69d1da1 (2026-08-13).
Data as JSON: /api/errors/b3cbde05869f9027.
Report an issue: GitHub.