MuntashirAkon/AppManager · error · IOException

Couldn't create symbolic link ${file} pointing to ${linkName

Error message

Couldn't create symbolic link ${file} pointing to ${linkName}

What it means

While extracting a tar entry that is a symbolic link, TarUtils.extract deletes any existing file at the target location and calls ExtendedFile.createNewSymbolicLink(linkName). If the OS-level symlink creation fails (returns false), it throws IOException("Couldn't create symbolic link <file> pointing to <linkName>"). Common underlying causes are filesystems that do not support symlinks (FAT/exFAT, some external storage) or lack of permission.

Source

Thrown at app/src/main/java/io/github/muntashirakon/AppManager/utils/TarUtils.java:188

                        file.delete();
                        continue;
                    }
                    // Check if the given entry is a link.
                    if (entry.isSymbolicLink() && file.getFilePath() != null) {
                        if ((!Paths.isUnderFilter(file, dest, filterPatterns) || Paths.willExclude(file, dest, exclusionPatterns))) {
                            // Do not create this link even if it is a directory
                            continue;
                        }
                        String linkName = entry.getLinkName();
                        // There's no need to check if the linkName exists as it may be extracted
                        // after the link has been created
                        // Special check for /data/app
                        if (linkName.startsWith("/data/app/")) {
                            linkName = getAbsolutePathToDataApp(linkName, realDataAppPath);
                        }
                        file.delete();
                        if (!file.createNewSymbolicLink(linkName)) {
                            throw new IOException("Couldn't create symbolic link " + file + " pointing to " + linkName);
                        }
                        continue;  // links do not need permission fixes
                    } else {
                        // Zip slip vulnerability might still be present
                        String realFilePath = file.getRealFilePath();
                        if (realDestPath != null && realFilePath != null && !realFilePath.startsWith(realDestPath)) {
                            throw new IOException("Zip slip vulnerability detected!" +
                                    "\nExpected dest: " + new File(realDestPath, entry.getName()) +
                                    "\nActual path: " + realFilePath);
                        }
                        if (!entry.isDirectory()) {
                            try (OutputStream os = file.openOutputStream()) {
                                IoUtils.copy(tis, os);
                            }
                        }
                    }
                    // Fix permissions
                    TarArchiveEntry finalEntry = entry;

View on GitHub (pinned to 0152f468fc)

Solutions

  1. Extract to internal app storage (context.getFilesDir()) which supports symlinks, instead of external/shared storage
  2. Check/create a test symlink in the destination beforehand and warn the user if unsupported
  3. Handle the failed createNewSymbolicLink by copying the link target's content as a regular file fallback
  4. Catch IOException around extract and report which file/link failed

Example fix

// before
TarUtils.extract(tarIn, destDir, ...);
// after
try {
    TarUtils.extract(tarIn, destDir, ...);
} catch (IOException e) {
    if (e.getMessage().startsWith("Couldn't create symbolic link")) {
        Log.w(TAG, "Symlinks unsupported on " + destDir + "; extracting without links");
        TarUtils.extract(tarIn, destDir, ... /* symlinkFallback=true or filter links */);
    } else throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

File probe = new File(destDir, ".symlink_probe");
boolean symlinksSupported = probe.createNewSymbolicLink("probe_target");
probe.delete();

Type guard

boolean supportsSymlinks(File dir) {
    File p = new File(dir, ".probe" + System.nanoTime());
    try { return p.createNewSymbolicLink("x") | p.delete(); } catch (IOException e) { return false; }
}

Try / catch

try {
    TarUtils.extract(in, dest, filters);
} catch (IOException e) {
    if (e.getMessage().startsWith("Couldn't create symbolic link")) {
        // fallback: re-extract with links skipped or copy link targets
    } else throw e;
}

Prevention

When it happens

Trigger: Extracting an archive containing symlink entries onto a destination filesystem without symlink support (vFAT SD card, /sdcard FUSE restrictions), or in a location the app lacks write/create permission for; Android may also block symlink creation depending on the mount and target path.

Common situations: Extracting app backups or APK splits with relative symlinks to external storage; restoring archives to shared storage that silently converts or rejects symlinks; SELinux policies denying symlink creation in the destination.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


AI-assisted analysis of MuntashirAkon/AppManager@0152f468fc (2026-09-12). Data as JSON: /api/errors/81e96e56a391497d. Report an issue: GitHub.