{"record":{"id":"40f83d757e29ab42","repo":"vercel/turborepo","slug":"unexpected-error-reading-package-json-at-works","errorCode":null,"errorMessage":"unexpected error reading \"package.json\" at ${workspaceRoot}","messagePattern":"unexpected error reading \"package\\.json\" at (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/turbo-workspaces/src/utils.ts","lineNumber":77,"sourceCode":"  try {\n    return readJsonSync(packageJsonPath, \"utf8\") as PackageJson;\n  } catch (err) {\n    if (err && typeof err === \"object\" && \"code\" in err) {\n      if (err.code === \"ENOENT\") {\n        throw new ConvertError(`no \"package.json\" found at ${workspaceRoot}`, {\n          type: \"package_json-missing\"\n        });\n      }\n      if (err.code === \"EJSONPARSE\") {\n        throw new ConvertError(\n          `failed to parse \"package.json\" at ${workspaceRoot}`,\n          {\n            type: \"package_json-parse_error\"\n          }\n        );\n      }\n    }\n    throw new Error(\n      `unexpected error reading \"package.json\" at ${workspaceRoot}`\n    );\n  }\n}\n\nfunction getWorkspacePackageManager({\n  workspaceRoot\n}: {\n  workspaceRoot: string;\n}): PackageManager | undefined {\n  const packageJson = getPackageJson({ workspaceRoot });\n  const { packageManager, devEngines } = packageJson;\n  if (packageManager) {\n    try {\n      const match = PACKAGE_MANAGER_REGEX.exec(packageManager);\n      if (match) {\n        const manager = match.groups?.manager;\n        return isPackageManager(manager) ? manager : undefined;","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/vercel/turborepo/blob/9f94a7d215b3881942527dd526afa3fc650869d5/packages/turbo-workspaces/src/utils.ts#L59-L95","documentation":"Catch-all thrown by getPackageJson() in turbo-workspaces when reading package.json fails with an errno that is neither ENOENT nor EJSONPARSE - e.g. EACCES (no read permission), EISDIR (package.json is a directory), ENOTDIR (a component of workspaceRoot is a file), EMFILE/ELOOP (fd exhaustion or symlink cycles). Unlike its siblings it is a plain `new Error(...)`, not a ConvertError, so it carries no `type` field and will not match ConvertError-based handling; the path is embedded in the message.","triggerScenarios":"workspaceRoot containing a regular-file segment where a directory is expected (e.g. passing `repo/package.json` or `repo/somefile.ts` as root -> ENOTDIR); package.json created as a directory by a bad script or installer (EISDIR); POSIX file modes like 600 owned by another user, or a container/CI user without read access (EACCES); EMFILE from opening many files with watchers also running.","commonSituations":"Scripts that join the wrong segments when computing the project root; Docker stages that COPY a directory named package.json by accident; repositories restored from archives that lose permissions; heavy watcher setups exhausting file descriptors so even reads fail; symlinked workspace roots that loop.","solutions":["Check what workspaceRoot actually is: `ls -la <workspaceRoot>` and `ls -la <workspaceRoot>/package.json` - it must be a regular file inside real directories","If it is EACCES/ownership: `chmod +r` the file and `chown` it (or run the command as the owning user/container user)","If ENOTDIR: correct the root argument - it must stop at the directory containing package.json, not include a file name","If EMFILE/ELOOP: raise ulimits (`ulimit -n 4096`) or remove the symlink cycle, close file-hungry processes, retry"],"exampleFix":"# before - a file segment inside the root path (ENOTDIR)\nawait getWorkspaceDetails({ root: \"repo/README.md\" });\n\n# after - root is the directory that owns package.json\nawait getWorkspaceDetails({ root: \"repo\" });","handlingStrategy":"try-catch","validationCode":"import { statSync, accessSync, constants } from \"node:fs\";\nimport path from \"node:path\";\n\nconst p = path.join(workspaceRoot, \"package.json\");\nconst st = statSync(p);            // throws ENOENT/ENOTDIR/ELOOP early\nif (!st.isFile()) throw new Error(`${p} is not a regular file`);\naccessSync(p, constants.R_OK);     // throws EACCES early","typeGuard":"// This error is a plain Error (no ConvertError.type), so narrow by shape\nfunction isUnexpectedPackageJsonRead(err: unknown): err is Error {\n  return err instanceof Error &&\n    !(err instanceof ConvertError) &&\n    err.message.includes('unexpected error reading \"package.json\"');\n}","tryCatchPattern":"try {\n  const details = await getWorkspaceDetails({ root });\n} catch (err) {\n  if (err instanceof Error && err.message.includes('unexpected error reading \"package.json\"')) {\n    // inspect the path in the message: stat it, check permissions/ownership\n    console.error(\"Filesystem-level failure reading package.json - check perms and that the path is a regular file\");\n    process.exit(1);\n  } else throw err;\n}","preventionTips":["Validate that workspaceRoot is a directory and package.json is a regular readable file before calling in","Run the tool as the user/container that owns the repo files","Avoid symlink cycles in workspace roots; keep ulimit -n comfortable when watchers are active","In Docker, ensure COPY did not materialize package.json as a directory"],"tags":["package-json","filesystem","permissions","workspace-root","errno"],"backgroundTag":"file-read-permission-denied","analyzedSha":"9f94a7d215b3881942527dd526afa3fc650869d5","analyzedAt":"2026-08-16T19:47:29.531Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}