Budibase/budibase · error · HTTPError

Project package contains unsupported links.

Error message

Project package contains unsupported links.

What it means

readDirectoryRecursively rejects any entry where entry.isSymbolicLink() is true, throwing this 400 HTTPError. Symbolic links inside an extracted archive could point anywhere on the host filesystem, so they are treated as unsafe and the import aborts.

Source

Thrown at packages/server/src/sdk/workspace/projects/backups/imports.ts:196

  totals = { files: 0, bytes: 0 }
): Promise<string[]> => {
  const entries = await fsp.readdir(dirPath, { withFileTypes: true })
  const files: string[] = []

  for (const entry of entries) {
    const fullPath = join(dirPath, entry.name)
    const relPath = relative(rootPath, fullPath)
    if (!isSafeArchivePath(relPath)) {
      throw new HTTPError("Project package contains unsafe paths.", 400)
    }
    if (relPath.split(/[\\/]/).length > MAX_PATH_SEGMENTS) {
      throw new HTTPError(
        "Project package contains paths that are too deep.",
        400
      )
    }
    if (entry.isSymbolicLink()) {
      throw new HTTPError("Project package contains unsupported links.", 400)
    }
    if (entry.isDirectory()) {
      files.push(
        ...(await readDirectoryRecursively(fullPath, rootPath, totals))
      )
    } else {
      const stats = await fsp.stat(fullPath)
      totals.files += 1
      totals.bytes += stats.size
      if (totals.files > MAX_PACKAGE_FILES) {
        throw new HTTPError("Project package contains too many files.", 400)
      }
      if (totals.bytes > MAX_EXTRACTED_SIZE_BYTES) {
        throw new HTTPError("Project package is too large.", 400)
      }
      files.push(fullPath)
    }
  }

View on GitHub (pinned to a81a902e9a)

Solutions

  1. Rebuild the package dereferencing symlinks (tar --dereference / cp -L) so all entries are real files
  2. Remove symlinked entries from the archive
  3. Create the package with Budibase's export flow instead of manual archiving
  4. Audit untrusted packages for links before importing

Example fix

// before
tar -czf app.tar.gz app/            # preserves symlinks
// after
tar -czf --dereference app.tar.gz app/   # stores link targets as regular files
Defensive patterns

Strategy: validation

Validate before calling

const entries = await fsp.readdir(dir, { withFileTypes: true })
if (entries.some(e => e.isSymbolicLink())) throw new Error("Package contains symlinks; dereference before importing")

Type guard

null

Try / catch

try {
  await importProject(packagePath)
} catch (e) {
  if (e instanceof HTTPError && e.message.includes("unsupported links")) {
    // rebuild archive with --dereference and retry
  } else throw e
}

Prevention

When it happens

Trigger: Importing a package whose extracted directory tree contains symlinks — commonly archives created with tar's default symlink preservation, or packages built on systems that use symlinks for shared assets/node_modules.

Common situations: Building packages with `tar -czf` over a tree containing symlinks; Unix build machines linking node_modules or static assets; tampered packages using links to escape the extraction root.

Related errors


AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29). Data as JSON: /api/errors/e42dfc763a379d7e. Report an issue: GitHub.