{"record":{"id":"b3cbde05869f9027","repo":"termux/termux-app","slug":"no-symlinks-txt-encountered","errorCode":null,"errorMessage":"No SYMLINKS.txt encountered","messagePattern":"No SYMLINKS\\.txt encountered","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"critical","filePath":"app/src/main/java/com/termux/app/TermuxInstaller.java","lineNumber":208,"sourceCode":"\n                                if (!isDirectory) {\n                                    try (FileOutputStream outStream = new FileOutputStream(targetFile)) {\n                                        int readBytes;\n                                        while ((readBytes = zipInput.read(buffer)) != -1)\n                                            outStream.write(buffer, 0, readBytes);\n                                    }\n                                    if (zipEntryName.startsWith(\"bin/\") || zipEntryName.startsWith(\"libexec\") ||\n                                        zipEntryName.startsWith(\"lib/apt/apt-helper\") || zipEntryName.startsWith(\"lib/apt/methods\")) {\n                                        //noinspection OctalInteger\n                                        Os.chmod(targetFile.getAbsolutePath(), 0700);\n                                    }\n                                }\n                            }\n                        }\n                    }\n\n                    if (symlinks.isEmpty())\n                        throw new RuntimeException(\"No SYMLINKS.txt encountered\");\n                    for (Pair<String, String> symlink : symlinks) {\n                        Os.symlink(symlink.first, symlink.second);\n                    }\n\n                    Logger.logInfo(LOG_TAG, \"Moving termux prefix staging to prefix directory.\");\n\n                    if (!TERMUX_STAGING_PREFIX_DIR.renameTo(TERMUX_PREFIX_DIR)) {\n                        throw new RuntimeException(\"Moving termux prefix staging to prefix directory failed\");\n                    }\n\n                    Logger.logInfo(LOG_TAG, \"Bootstrap packages installed successfully.\");\n\n                    // Recreate env file since termux prefix was wiped earlier\n                    TermuxShellEnvironment.writeEnvironmentToFile(activity);\n\n                    activity.runOnUiThread(whenDone);\n\n                } catch (final Exception e) {","sourceCodeStart":190,"sourceCodeEnd":226,"githubUrl":"https://github.com/termux/termux-app/blob/3df69d1da197dd9bd71a3bafd902dffd720576b4/app/src/main/java/com/termux/app/TermuxInstaller.java#L190-L226","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nif (symlinks.isEmpty())\n    throw new RuntimeException(\"No SYMLINKS.txt encountered\");\n\n// after (diagnostic: list entry names seen so the cause is obvious)\nif (symlinks.isEmpty())\n    throw new RuntimeException(\"No SYMLINKS.txt encountered in bootstrap zip. Entries seen: \" + seenEntryNames);","handlingStrategy":"validation","validationCode":"// Before parsing, verify the zip contains SYMLINKS.txt\nboolean hasManifest = false;\ntry (ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(zipBytes))) {\n    ZipEntry e;\n    while ((e = zis.getNextEntry()) != null) {\n        if (\"SYMLINKS.txt\".equals(e.getName())) { hasManifest = true; break; }\n    }\n}\nif (!hasManifest) {\n    throw new IOException(\"Bootstrap zip missing SYMLINKS.txt manifest\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["bootstrap","zip-parsing","installer","integrity-check","termux"],"backgroundTag":null,"analyzedSha":"3df69d1da197dd9bd71a3bafd902dffd720576b4","analyzedAt":"2026-08-13T22:46:28.294Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}