{"record":{"id":"9f17a94f3d10a31e","repo":"angular/angular-cli","slug":"path-p-is-a-directory","errorCode":null,"errorMessage":"Path \"${p}\" is a directory.","messagePattern":"Path \"(.+?)\" is a directory\\.","errorType":"exception","errorClass":"PathIsDirectoryException","httpStatus":null,"severity":"error","filePath":"packages/angular_devkit/schematics/src/tree/host-tree.ts","lineNumber":345,"sourceCode":"    // If there is a parse error throw with the error information\n    if (errors[0]) {\n      const { error, offset } = errors[0];\n      throw new Error(\n        `Failed to parse \"${path}\" as JSON. ${printParseErrorCode(error)} at offset: ${offset}.`,\n      );\n    }\n\n    return result;\n  }\n\n  exists(path: string): boolean {\n    return this._recordSync.isFile(this._normalizePath(path));\n  }\n\n  get(path: string): FileEntry | null {\n    const p = this._normalizePath(path);\n    if (this._recordSync.isDirectory(p)) {\n      throw new PathIsDirectoryException(p);\n    }\n    if (!this._recordSync.exists(p)) {\n      return null;\n    }\n\n    return new LazyFileEntry(p, () => Buffer.from(this._recordSync.read(p)));\n  }\n\n  getDir(path: string): DirEntry {\n    const p = this._normalizePath(path);\n    if (this._recordSync.isFile(p)) {\n      throw new PathIsFileException(p);\n    }\n\n    let maybeCache = this._dirCache.get(p);\n    if (!maybeCache) {\n      let parent: Path | null = dirname(p);\n      if (p === parent) {","sourceCodeStart":327,"sourceCodeEnd":363,"githubUrl":"https://github.com/angular/angular-cli/blob/bb72145f9ab45aee29f523236b3a25cd0813a841/packages/angular_devkit/schematics/src/tree/host-tree.ts#L327-L363","documentation":"HostTree.get(path) returns the FileEntry for a file, or null if it doesn't exist — but if the path points to a directory it throws PathIsDirectoryException with this message. The API distinguishes 'missing' (null) from 'wrong type' (throw), because a directory has no file content to return.","triggerScenarios":"Calling tree.get(path) or tree.read(path) with a path that resolves to a directory in the virtual tree, e.g. a directory-style import path ('/src/assets' with no file extension) or passing tree.root.path.","commonSituations":"Resolving module specifiers that omit the filename; glob/visitor logic that forwards directory paths into get; misconfigured template paths pointing at folders.","solutions":["Check tree.exists(path) first — it returns true only for files — and handle directories with tree.getDir(path).","Append the actual filename to the path before calling get.","If the path may be either, branch: if tree.getDir(path) doesn't throw, treat it as a directory."],"exampleFix":"// before\nconst entry = tree.get(dirPath);\n// after\nif (tree.exists(dirPath)) {\n  const entry = tree.get(dirPath);\n} else if (tree.getDir(dirPath)) {\n  // it's a directory: use DirEntry APIs instead\n}","handlingStrategy":"validation","validationCode":"if (tree.exists(p)) { /* it's a file: safe to tree.get(p) */ }\nelse if (!tree.getDir.exists) { /* neither */ }","typeGuard":"function isFileEntry(tree: Tree, path: string): boolean {\n  return tree.exists(path); // exists() is file-only in HostTree\n}","tryCatchPattern":"let entry;\ntry {\n  entry = tree.get(p);\n} catch (e) {\n  if ((e as any).constructor?.name === 'PathIsDirectoryException') {\n    const dir = tree.getDir(p); // handle directory case\n  } else throw e;\n}","preventionTips":["Always resolve module paths to concrete file names before tree.get","Use tree.exists() (file-only) rather than raw get to distinguish","Handle directories via DirEntry APIs (visit files, then get each)","Log the normalized path when building paths dynamically"],"tags":["path","directory","wrong-type","schematics"],"backgroundTag":"path-is-directory","analyzedSha":"bb72145f9ab45aee29f523236b3a25cd0813a841","analyzedAt":"2026-08-30T02:47:34.745Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}