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

  1. Change the asset's "output" in angular.json so it stays inside the build output directory (e.g. "output": "public" instead of "../public").
  2. 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).
  3. 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

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


AI-assisted analysis of angular/angular-cli@bb72145f9a (2026-08-30). Data as JSON: /api/errors/f3d671fb4ca485f5. Report an issue: GitHub.