{"record":{"id":"4fcbb2097afbe35f","repo":"termux/termux-app","slug":"moving-termux-prefix-staging-to-prefix-directory-f","errorCode":null,"errorMessage":"Moving termux prefix staging to prefix directory failed","messagePattern":"Moving termux prefix staging to prefix directory failed","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"critical","filePath":"app/src/main/java/com/termux/app/TermuxInstaller.java","lineNumber":216,"sourceCode":"                                        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) {\n                    showBootstrapErrorDialog(activity, whenDone, Logger.getStackTracesMarkdownString(null, Logger.getStackTracesStringArray(e)));\n\n                } finally {\n                    activity.runOnUiThread(() -> {\n                        try {\n                            progress.dismiss();\n                        } catch (RuntimeException e) {\n                            // Activity already dismissed - ignore.","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/termux/termux-app/blob/3df69d1da197dd9bd71a3bafd902dffd720576b4/app/src/main/java/com/termux/app/TermuxInstaller.java#L198-L234","documentation":"Thrown when File.renameTo() from the staging prefix directory to the final prefix directory returns false. renameTo can fail across filesystems/mount points, when the destination already exists and is non-empty, or on permission/SELinux denial. The staged bootstrap is fully built but cannot be atomically moved into place.","triggerScenarios":"TERMUX_STAGING_PREFIX_DIR and TERMUX_PREFIX_DIR live on different filesystems so renameTo cannot do a cross-device move; the destination prefix dir already exists with leftover files; a parent directory lacks write permission; SELinux policy denies the rename; another process holds files open in the destination.","commonSituations":"A previous failed install left a partial TERMUX_PREFIX_DIR behind; device-specific filesystem layout where staging and prefix resolve to different mounts; storage permission revoked; antivirus or another app locked files under the prefix.","solutions":["Delete any pre-existing TERMUX_PREFIX_DIR before the rename so the destination path does not exist.","Ensure both staging and prefix paths share the same filesystem (both under the app's private files dir).","Replace renameTo with a copy-then-delete fallback when renameTo returns false, since cross-device rename is not atomic.","Check logcat for SELinux denials or permission errors around the rename call."],"exampleFix":"// before\nif (!TERMUX_STAGING_PREFIX_DIR.renameTo(TERMUX_PREFIX_DIR)) {\n    throw new RuntimeException(\"Moving termux prefix staging to prefix directory failed\");\n}\n\n// after (fallback copy when rename fails, e.g. cross-device)\nif (!TERMUX_STAGING_PREFIX_DIR.renameTo(TERMUX_PREFIX_DIR)) {\n    try {\n        org.apache.commons.io.FileUtils.moveDirectory(TERMUX_STAGING_PREFIX_DIR, TERMUX_PREFIX_DIR);\n    } catch (IOException moveEx) {\n        throw new RuntimeException(\"Moving termux prefix staging to prefix directory failed\", moveEx);\n    }\n}","handlingStrategy":"fallback","validationCode":"// Pre-flight: ensure destination does not exist and both paths share a filesystem\nif (TERMUX_PREFIX_DIR.exists()) {\n    deleteRecursively(TERMUX_PREFIX_DIR);\n}\nboolean sameFs = TERMUX_STAGING_PREFIX_DIR.getParent().equals(TERMUX_PREFIX_DIR.getParent());","typeGuard":null,"tryCatchPattern":"try {\n    if (!TERMUX_STAGING_PREFIX_DIR.renameTo(TERMUX_PREFIX_DIR)) {\n        copyDirectory(TERMUX_STAGING_PREFIX_DIR, TERMUX_PREFIX_DIR);\n        deleteRecursively(TERMUX_STAGING_PREFIX_DIR);\n    }\n} catch (IOException moveEx) {\n    showBootstrapErrorDialog(activity, whenDone, \"Failed to move prefix: \" + moveEx.getMessage());\n}","preventionTips":["Always remove a pre-existing destination prefix before renaming.","Keep staging and final prefix on the same filesystem to make rename atomic.","Implement a copy+delete fallback since renameTo is filesystem-dependent."],"tags":["bootstrap","filesystem","installer","rename","termux"],"backgroundTag":null,"analyzedSha":"3df69d1da197dd9bd71a3bafd902dffd720576b4","analyzedAt":"2026-08-13T22:46:28.294Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}