{"record":{"id":"1cf24790f394a95f","repo":"mastra-ai/mastra","slug":"path-is-a-directory","errorCode":null,"errorMessage":"Path is a directory","messagePattern":"Path is a directory","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mastracode/factory/src/routes/fs.ts","lineNumber":371,"sourceCode":"  return {\n    workspacePath: workspace,\n    root: safeRoot,\n    rootPath: confinedRootPath,\n    entries: await listRenderedEntries(confinedRootPath),\n  };\n}\n\nexport async function readWorkspaceFile(root: string, workspacePath: string, path: string): Promise<WorkspaceFile> {\n  const safePath = assertRelativePath(path, 'path');\n  const relativeRoot = safePath.split('/')[0] ?? '';\n  assertApprovedRenderedRoot(relativeRoot);\n  const {\n    workspace,\n    path: confinedPath,\n    relativePath,\n  } = await confinedWorkspaceRelativePath(root, workspacePath, path);\n  const info = await lstat(confinedPath);\n  if (info.isDirectory()) throw new Error('Path is a directory');\n  if (!info.isFile()) throw new Error('Unsupported file type');\n\n  const bytesToRead = Math.min(info.size, MAX_TEXT_FILE_BYTES);\n  const contentBuffer = Buffer.alloc(bytesToRead);\n  const handle = await open(confinedPath, 'r');\n  try {\n    await handle.read(contentBuffer, 0, bytesToRead, 0);\n  } finally {\n    await handle.close();\n  }\n\n  try {\n    const content = TEXT_DECODER.decode(contentBuffer);\n    return {\n      workspacePath: workspace,\n      path: relativePath,\n      name: relativePath.split('/').pop() ?? relativePath,\n      size: info.size,","sourceCodeStart":353,"sourceCodeEnd":389,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/factory/src/routes/fs.ts#L353-L389","documentation":"readWorkspaceFile reads a text file from a workspace; after resolving and confining the path it lstats the target and refuses to read directories. This error is thrown when the requested path exists inside the workspace but is a directory rather than a regular file, because reading it as file content is meaningless. The caller should use a directory-listing endpoint instead.","triggerScenarios":"Calling readWorkspaceFile (or the fs read route) with a path query that names an existing directory inside the workspace, e.g. path=src or path=.artifacts.","commonSituations":"Passing a folder path copied from a listing response instead of an entry file path; assuming the artifacts root itself is readable as a file; UI bugs that don't distinguish entry.type === 'directory' before opening a reader.","solutions":["Check that the path targets a file, not a directory (ls or stat the path inside the workspace).","If you want the directory contents, use the directory-listing / rendered-listing endpoint for that workspace instead.","Fix client code to branch on the entry type from listing results before calling the read endpoint.","If you expected a file, verify the file wasn't replaced by a directory of the same name during a build or checkout."],"exampleFix":"// before: reading a directory\nGET /fs/file?path=.artifacts            // → 'Path is a directory'\n// after: read a file inside it\nGET /fs/file?path=.artifacts/report.txt","handlingStrategy":"validation","validationCode":"import { stat } from 'node:fs/promises';\nasync function isFileInWorkspace(workspace: string, rel: string): Promise<boolean> {\n  try {\n    const info = await stat(resolve(workspace, rel));\n    return info.isFile();\n  } catch {\n    return false;\n  }\n}\n// only call readWorkspaceFile when this returns true; else use the listing endpoint","typeGuard":"function isFileEntry(entry: { type: string }): entry is { type: 'file' } {\n  return entry.type === 'file';\n}","tryCatchPattern":"try {\n  const file = await readWorkspaceFile(root, ws, path);\n} catch (e) {\n  if (e instanceof Error && e.message === 'Path is a directory') {\n    return listWorkspaceRenderedPath(root, ws, path); // fall back to listing\n  }\n  throw e;\n}","preventionTips":["Branch on entry.type from listing results before opening a file reader.","Never request the rendered root itself ('.artifacts', 'src') as a file path.","Verify the target with stat before read in scripts that build paths dynamically.","Watch for directories replacing files of the same name after builds/checkouts."],"tags":["filesystem","validation","wrong-argument-type"],"backgroundTag":"path-is-a-directory","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}