{"record":{"id":"336c5ee72b61322a","repo":"Budibase/budibase","slug":"project-package-contains-paths-that-are-too-deep","errorCode":null,"errorMessage":"Project package contains paths that are too deep.","messagePattern":"Project package contains paths that are too deep\\.","errorType":"http","errorClass":"HTTPError","httpStatus":400,"severity":"error","filePath":"packages/server/src/sdk/workspace/projects/backups/imports.ts","lineNumber":190,"sourceCode":"  )\n}\n\nconst readDirectoryRecursively = async (\n  dirPath: string,\n  rootPath = dirPath,\n  totals = { files: 0, bytes: 0 }\n): Promise<string[]> => {\n  const entries = await fsp.readdir(dirPath, { withFileTypes: true })\n  const files: string[] = []\n\n  for (const entry of entries) {\n    const fullPath = join(dirPath, entry.name)\n    const relPath = relative(rootPath, fullPath)\n    if (!isSafeArchivePath(relPath)) {\n      throw new HTTPError(\"Project package contains unsafe paths.\", 400)\n    }\n    if (relPath.split(/[\\\\/]/).length > MAX_PATH_SEGMENTS) {\n      throw new HTTPError(\n        \"Project package contains paths that are too deep.\",\n        400\n      )\n    }\n    if (entry.isSymbolicLink()) {\n      throw new HTTPError(\"Project package contains unsupported links.\", 400)\n    }\n    if (entry.isDirectory()) {\n      files.push(\n        ...(await readDirectoryRecursively(fullPath, rootPath, totals))\n      )\n    } else {\n      const stats = await fsp.stat(fullPath)\n      totals.files += 1\n      totals.bytes += stats.size\n      if (totals.files > MAX_PACKAGE_FILES) {\n        throw new HTTPError(\"Project package contains too many files.\", 400)\n      }","sourceCodeStart":172,"sourceCodeEnd":208,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/sdk/workspace/projects/backups/imports.ts#L172-L208","documentation":"During package import, each file's relative path is split on slashes and compared against MAX_PATH_SEGMENTS. Deeper nesting than allowed is rejected with this 400 HTTPError to bound resource usage and avoid filesystem limits. It fires after the safety check and before symlink handling in readDirectoryRecursively.","triggerScenarios":"Importing a project package that contains files nested more than MAX_PATH_SEGMENTS directories deep — e.g. node_modules-style trees, generated code bundles, or archives that embed several directory layers inside the package.","commonSituations":"Zipping an entire app folder including dependency/build output; automating exports that capture extra nested directories; malicious archives engineered to be pathologically deep.","solutions":["Flatten the package structure so files sit within the allowed depth before archiving","Exclude generated directories (node_modules, build output) from the package","Check the source app for unusually deep resource naming and simplify","If legitimately needed, raise MAX_PATH_SEGMENTS in a fork — not recommended"],"exampleFix":"// before\n// package contains: files/a/b/c/d/e/f/g/h/deep.json (9+ segments)\n// after\n// restructure to: files/generated/deep.json (within MAX_PATH_SEGMENTS)","handlingStrategy":"validation","validationCode":"const segments = relPath.split(/[\\\\/]/).length\nif (segments > MAX_PATH_SEGMENTS) throw new Error(`Path too deep (${segments} segments): ${relPath}`)","typeGuard":"null","tryCatchPattern":"try {\n  await importProject(packagePath)\n} catch (e) {\n  if (e instanceof HTTPError && e.message.includes(\"too deep\")) {\n    // flatten package structure or exclude deep generated trees, then re-import\n  } else throw e\n}","preventionTips":["Exclude node_modules/build output from packages","Keep package directory nesting shallow","Pre-flight check the deepest path length with tar -tf"],"tags":["import","validation","path-depth","backups"],"backgroundTag":"path-too-deep","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}