quasarframework/quasar · critical · Error

Refusing to remove a filesystem root as build output

Error message

Refusing to remove a filesystem root as build output

What it means

As a destructive-operation guard, getBuildArtifactsCleanTarget refuses to treat a filesystem root ('/' on POSIX, drive root on Windows) as build output. It compares both the raw resolved target and its realpath-resolved effective path (catching symlinks that point at a root) against isFilesystemRoot.

Solutions

  1. Set distDir to a real subdirectory like 'dist' or 'build/output'
  2. Check for symlinks in the configured path ('ls -l') and repoint them away from the root
  3. Never interpolate unvalidated env vars directly into distDir; default to 'dist' when empty

Example fix

// before
distDir: process.env.DIST_DIR || '/'
// after
distDir: process.env.DIST_DIR || 'dist'
Defensive patterns

Strategy: validation

Validate before calling

const path = require('node:path')
const { parse } = require('node:path')
function isFsRoot(p) {
  const parsed = parse(path.resolve(p))
  return parsed.root === path.resolve(p) && (parsed.dir === parsed.root)
}
const target = path.resolve(projectDir, distDir)
if (isFsRoot(target)) throw new Error('dist dir resolves to filesystem root')

Try / catch

try {
  await removeBuildArtifacts(opts)
} catch (err) {
  if (err.message === 'Refusing to remove a filesystem root as build output') {
    // fix distDir config; never bypass this guard
  } else throw err
}

Prevention

When it happens

Trigger: Configuring the build output directory as '/' (or 'C:/' etc.), or as a path that resolves via symlinks to a filesystem root — e.g. distDir pointing at a symlink named /dist that actually targets /.

Common situations: Setting distDir: '/' by mistake in quasar.config.js; a symlink farm or Docker volume mount mapping the dist dir to the container root; reading an empty environment variable that collapses the path to root after resolve.

Related errors


AI-assisted analysis of quasarframework/quasar@4841521b5f (2026-08-30). Data as JSON: /api/errors/03844005c88aafae. Report an issue: GitHub.

Appendix: source

Thrown at app-vite/lib/utils/remove-build-artifacts.js:72

  projectDir,
  allowOutsideProject
}) {
  if (typeof targetDir !== 'string' || targetDir.trim() === '') {
    throw new Error('Build output directory must be a non-empty path')
  }

  if (typeof projectDir !== 'string' || projectDir.trim() === '') {
    throw new Error('Project directory must be a non-empty path')
  }

  const project = resolve(projectDir)
  const effectiveProject = fse.realpathSync(project)
  const home = fse.realpathSync(resolve(homedir()))
  const target = resolve(projectDir, targetDir)
  const effectiveTarget = getEffectivePath(target)

  if (isFilesystemRoot(target) || isFilesystemRoot(effectiveTarget)) {
    throw new Error('Refusing to remove a filesystem root as build output')
  }

  if (target === home || effectiveTarget === home) {
    throw new Error(
      'Refusing to remove the user home directory as build output'
    )
  }

  if (target === project || effectiveTarget === effectiveProject) {
    throw new Error('Refusing to remove the project root as build output')
  }

  if (
    allowOutsideProject !== true &&
    isContainedPath(effectiveProject, effectiveTarget) === false
  ) {
    throw new Error(
      'Build output directory must remain inside the project. Set build.allowOutsideProjectDistDir to true to explicitly allow an external directory.'

View on GitHub (pinned to 4841521b5f)