{"record":{"id":"f83f72d2e1347e27","repo":"quasarframework/quasar","slug":"build-output-directory-must-be-a-non-empty-path","errorCode":null,"errorMessage":"Build output directory must be a non-empty path","messagePattern":"Build output directory must be a non-empty path","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"app-vite/lib/utils/remove-build-artifacts.js","lineNumber":58,"sourceCode":"\nfunction getEffectivePath(target) {\n  const existingAncestor = getExistingAncestor(target)\n  const realAncestor = fse.realpathSync(existingAncestor)\n\n  return resolve(realAncestor, relative(existingAncestor, target))\n}\n\nfunction isFilesystemRoot(target) {\n  return target === parse(target).root\n}\n\nexport function getBuildArtifactsCleanTarget({\n  targetDir,\n  projectDir,\n  allowOutsideProject\n}) {\n  if (typeof targetDir !== 'string' || targetDir.trim() === '') {\n    throw new Error('Build output directory must be a non-empty path')\n  }\n\n  if (typeof projectDir !== 'string' || projectDir.trim() === '') {\n    throw new Error('Project directory must be a non-empty path')\n  }\n\n  const project = resolve(projectDir)\n  const effectiveProject = fse.realpathSync(project)\n  const home = fse.realpathSync(resolve(homedir()))\n  const target = resolve(projectDir, targetDir)\n  const effectiveTarget = getEffectivePath(target)\n\n  if (isFilesystemRoot(target) || isFilesystemRoot(effectiveTarget)) {\n    throw new Error('Refusing to remove a filesystem root as build output')\n  }\n\n  if (target === home || effectiveTarget === home) {\n    throw new Error(","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/quasarframework/quasar/blob/4841521b5f635a971eb9e2710e5542efd194691b/app-vite/lib/utils/remove-build-artifacts.js#L40-L76","documentation":"getBuildArtifactsCleanTarget validates the options for the build-artifact removal step before touching the filesystem. It throws 'Build output directory must be a non-empty path' when targetDir is not a non-empty (after trim) string, because deleting an empty/invalid target would be ambiguous and dangerous.","triggerScenarios":"Passing targetDir as undefined, null, a non-string, '' or whitespace-only to the clean-artifacts API — typically when build.buildDir/distDir is missing from the resolved config or an option was misread.","commonSituations":"A quasar.config.js with no distDir and a plugin reading it as undefined; programmatic use of the cleaner with a hand-built options object omitting targetDir; config parsing returning empty strings for empty config values.","solutions":["Set a valid distDir/build output directory in quasar.config.js (e.g. distDir: 'dist')","Check that the code populating targetDir actually resolves the value (log it before the call)","Ensure the option key name matches the API (targetDir) and is a string, not a path object"],"exampleFix":"// before\nawait removeBuildArtifacts({ targetDir: undefined, projectDir: process.cwd() })\n// after\nawait removeBuildArtifacts({ targetDir: 'dist', projectDir: process.cwd() })","handlingStrategy":"validation","validationCode":"function assertNonEmptyString(v, name) {\n  if (typeof v !== 'string' || v.trim() === '') throw new TypeError(`${name} must be a non-empty string`)\n}\nassertNonEmptyString(opts.targetDir, 'targetDir')","typeGuard":"function isNonEmptyString(v) {\n  return typeof v === 'string' && v.trim() !== ''\n}\n// usage: if (isNonEmptyString(opts.targetDir)) { ... }","tryCatchPattern":"try {\n  await removeBuildArtifacts(opts)\n} catch (err) {\n  if (err.message === 'Build output directory must be a non-empty path') {\n    // default to 'dist' or surface a config error\n  } else throw err\n}","preventionTips":["Default targetDir/distDir to 'dist' when unset","Validate config values right after loading quasar.config.js","Avoid interpolating possibly-empty env vars directly into path options"],"tags":["validation","config","path"],"backgroundTag":"invalid-path-option","analyzedSha":"4841521b5f635a971eb9e2710e5542efd194691b","analyzedAt":"2026-08-30T01:13:14.944Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}