arduino/Arduino · error · IOException

Invalid archive: it must contain a single root folder while

Error message

Invalid archive: it must contain a single root folder while file {linkName} is outside {pathPrefix}

What it means

For archive entries that are links (hard or symbolic), the link's target path (linkName) must also lie inside the stripped root prefix. If the link points outside the root folder — a classic path-traversal / symlink-attack shape — extract() throws this IOException naming the link target and the expected prefix.

Source

Thrown at arduino-core/src/cc/arduino/utils/ArchiveExtractor.java:191

            stripPath--;
          }
          pathPrefix = name.substring(0, slash);
        }

        // Strip the common path prefix when requested
        if (!name.startsWith(pathPrefix)) {
          throw new IOException("Invalid archive: it must contain a single root folder while file " + name + " is outside " + pathPrefix);
        }
        name = name.substring(pathPrefix.length());
        if (name.isEmpty()) {
          continue;
        }
        File outputFile = new File(destFolder, name);

        File outputLinkedFile = null;
        if (isLink) {
          if (!linkName.startsWith(pathPrefix)) {
            throw new IOException("Invalid archive: it must contain a single root folder while file " + linkName + " is outside " + pathPrefix);
          }
          linkName = linkName.substring(pathPrefix.length());
          outputLinkedFile = new File(destFolder, linkName);
        }
        if (isSymLink) {
          // Symbolic links are referenced with relative paths
          outputLinkedFile = new File(linkName);
          if (outputLinkedFile.isAbsolute()) {
            System.err.println(I18n.format(tr("Warning: file {0} links to an absolute path {1}"), outputFile, outputLinkedFile));
            System.err.println();
          }
        }

        // Safety check
        if (isDirectory) {
          if (outputFile.isFile() && !overwrite) {
            throw new IOException("Can't create folder " + outputFile + ", a file with the same name exists!");
          }

View on GitHub (pinned to a0df6e0e83)

Solutions

  1. Repackage the archive with only relative links that stay inside the root folder (or remove links entirely)
  2. Inspect with tar -tvf to find suspicious absolute or '../' link targets and fix them before distributing
  3. If you just need the files, extract with tar manually and resolve/remove the offending links

Example fix

// before (inside tar)
ln -s /etc/passwd lib/config -> link outside root
// after
ln -s ../shared/config lib/config -> relative link within root folder
Defensive patterns

Strategy: validation

Validate before calling

// reject archives whose link targets are absolute or escape the root
try (TarArchiveInputStream in = new TarArchiveInputStream(new FileInputStream(archiveFile))) {
  TarArchiveEntry e;
  while ((e = in.getNextTarEntry()) != null) {
    if (e.isLink() && (e.getLinkName().startsWith("/") || e.getLinkName().contains(".."))) throw new SecurityException("Unsafe link target: " + e.getLinkName());
  }
}

Try / catch

try { extractor.extract(archive, dest, 1); } catch (IOException e) { if (e.getMessage().contains("is outside")) { rejectOrSanitizeArchive(archive); } else { throw e; } }

Prevention

When it happens

Trigger: A tar entry is a link whose target path (linkName) does not start with the computed root pathPrefix, e.g. a symlink pointing to '../..' or to an absolute path outside the archive's root folder.

Common situations: Archives that bundle absolute or parent-relative symlinks (common when tarring from odd locations or packaging artifacts referencing the build machine); malicious archives attempting symlink escape; developer archives containing build symlinks to toolchains.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of arduino/Arduino@a0df6e0e83 (2026-09-06). Data as JSON: /api/errors/717845d4cc4d11f8. Report an issue: GitHub.