{"record":{"id":"f3d671fb4ca485f5","repo":"angular/angular-cli","slug":"an-asset-cannot-be-written-to-a-location-outside-o-f3d671","errorCode":null,"errorMessage":"An asset cannot be written to a location outside of the output path.","messagePattern":"An asset cannot be written to a location outside of the output path\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/angular_devkit/build_angular/src/utils/normalize-asset-patterns.ts","lineNumber":82,"sourceCode":"      }\n\n      // Output directory for both is the relative path from source root to input.\n      const output = path.relative(resolvedSourceRoot, path.resolve(workspaceRoot, input));\n\n      assetPattern = { glob, input, output };\n    } else {\n      const resolvedInput = path.resolve(workspaceRoot, assetPattern.input);\n      if (!resolvedInput.startsWith(workspaceRoot)) {\n        throw new Error(`The ${assetPattern.input} asset path must be within the workspace root.`);\n      }\n\n      assetPattern.output = path.join('.', assetPattern.output ?? '');\n    }\n\n    assert(assetPattern.output !== undefined);\n\n    if (assetPattern.output.startsWith('..')) {\n      throw new Error('An asset cannot be written to a location outside of the output path.');\n    }\n\n    return assetPattern as AssetPatternClass & { output: string };\n  });\n}\n","sourceCodeStart":64,"sourceCodeEnd":88,"githubUrl":"https://github.com/angular/angular-cli/blob/bb72145f9ab45aee29f523236b3a25cd0813a841/packages/angular_devkit/build_angular/src/utils/normalize-asset-patterns.ts#L64-L88","documentation":"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.","triggerScenarios":"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('.', ...).","commonSituations":"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.","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."],"exampleFix":"// before (angular.json assets)\n{ \"glob\": \"**/*\", \"input\": \"src/assets\", \"output\": \"../shared/static\" }\n// after\n{ \"glob\": \"**/*\", \"input\": \"src/assets\", \"output\": \"shared/static\" }","handlingStrategy":"validation","validationCode":"// Pre-check every asset output before invoking the build\nfunction validateAssetOutputs(assets = [], workspaceRoot, outputPath) {\n  for (const a of assets) {\n    const out = require('path').join(outputPath, a.output ?? '');\n    const rel = require('path').relative(outputPath, out);\n    if (rel.startsWith('..') || require('path').isAbsolute(rel)) {\n      throw new Error(`Asset output '${a.output}' escapes the build output path '${outputPath}'.`);\n    }\n  }\n}","typeGuard":"function isAssetInsideOutput(asset, outputPath) {\n  const rel = require('path').relative(outputPath, require('path').join(outputPath, asset.output ?? ''));\n  return !rel.startsWith('..') && !require('path').isAbsolute(rel);\n}","tryCatchPattern":"try {\n  await builder.execute(normalizedSchema);\n} catch (err) {\n  if (err?.message === 'An asset cannot be written to a location outside of the output path.') {\n    // fix asset config or surface a friendly message; do not retry blindly\n  } else throw err;\n}","preventionTips":["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."],"tags":["angular-cli","assets","configuration","path-traversal"],"backgroundTag":"asset-output-path-escape","analyzedSha":"bb72145f9ab45aee29f523236b3a25cd0813a841","analyzedAt":"2026-08-30T02:47:34.745Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}