angular/angular-cli · error · Error
An asset cannot be written to a location outside of the outp
Error message
An asset cannot be written to a location outside of the output path.
What it means
During build initialization, normalizeAssetPatterns rewrites every asset pattern's `output` destination to be rooted at the build output directory (path.join('.', output)). If, after normalization, the output path still starts with '..' (i.e. it escapes the output folder), this error is thrown to protect the build from writing generated assets outside the controlled output directory. It is a fail-fast config validation so no files are emitted to arbitrary locations.
Source
Thrown at packages/angular_devkit/build_angular/src/utils/normalize-asset-patterns.ts:82
}
// Output directory for both is the relative path from source root to input.
const output = path.relative(resolvedSourceRoot, path.resolve(workspaceRoot, input));
assetPattern = { glob, input, output };
} else {
const resolvedInput = path.resolve(workspaceRoot, assetPattern.input);
if (!resolvedInput.startsWith(workspaceRoot)) {
throw new Error(`The ${assetPattern.input} asset path must be within the workspace root.`);
}
assetPattern.output = path.join('.', assetPattern.output ?? '');
}
assert(assetPattern.output !== undefined);
if (assetPattern.output.startsWith('..')) {
throw new Error('An asset cannot be written to a location outside of the output path.');
}
return assetPattern as AssetPatternClass & { output: string };
});
}
View on GitHub (pinned to bb72145f9a)
Solutions
- Change the asset's "output" in angular.json so it stays inside the build output directory (e.g. "output": "public" instead of "../public").
- If the files must land outside the build output, emit them inside dist and copy them afterwards with a post-build script (e.g. cp -r dist/app/public ../backend/static).
- Check for accidental leading "../" segments or typos in the glob/output pair; the output is resolved relative to the output path, not the project root.
Example fix
// before (angular.json assets)
{ "glob": "**/*", "input": "src/assets", "output": "../shared/static" }
// after
{ "glob": "**/*", "input": "src/assets", "output": "shared/static" } Defensive patterns
Strategy: validation
Validate before calling
// Pre-check every asset output before invoking the build
function validateAssetOutputs(assets = [], workspaceRoot, outputPath) {
for (const a of assets) {
const out = require('path').join(outputPath, a.output ?? '');
const rel = require('path').relative(outputPath, out);
if (rel.startsWith('..') || require('path').isAbsolute(rel)) {
throw new Error(`Asset output '${a.output}' escapes the build output path '${outputPath}'.`);
}
}
} Type guard
function isAssetInsideOutput(asset, outputPath) {
const rel = require('path').relative(outputPath, require('path').join(outputPath, asset.output ?? ''));
return !rel.startsWith('..') && !require('path').isAbsolute(rel);
} Try / catch
try {
await builder.execute(normalizedSchema);
} catch (err) {
if (err?.message === 'An asset cannot be written to a location outside of the output path.') {
// fix asset config or surface a friendly message; do not retry blindly
} else throw err;
} Prevention
- Keep asset "output" values relative and free of ".." segments in angular.json.
- Copy assets to directories outside dist with a post-build step, not asset patterns.
- Lint angular.json in CI to assert no asset output path normalizes outside outputPath.
When it happens
Trigger: Running ng build / buildWebpackBrowser with an assetPattern whose `output` resolves to a path beginning with '..' — e.g. "output": "../public" or a path like "./nested/../../outside" that still normalizes outside the output path after path.join('.', ...).
Common situations: Copying assets into a sibling directory of dist ("../backend/static"), copying the output from a previous monorepo layout, or migrating config from other bundlers where an absolute or parent-relative glob destination is common.
Related errors
- Access denied: path '${path}' is outside allowed roots.
- Access denied: glob pattern '${pattern}' contains path trave
- Unsupported package manager: "${name}"
- The configured package manager, '${this.descriptor.binary}',
- Project "${project}" does not exist.
AI-assisted analysis of angular/angular-cli@bb72145f9a (2026-08-30).
Data as JSON: /api/errors/f3d671fb4ca485f5.
Report an issue: GitHub.