{"record":{"id":"c23590b1014dcee4","repo":"agalwood/Motrix","slug":"plugin-fs-path-too-long","errorCode":"plugin.fs.path_too_long","errorMessage":"plugin.fs.path_too_long: path exceeds ${PATH_MAX} characters","messagePattern":"plugin\\.fs\\.path_too_long: path exceeds (.+?) characters","errorType":"validation","errorClass":"FsSandboxError","httpStatus":null,"severity":"error","filePath":"src/core/plugin/capabilities/fs-sandbox.ts","lineNumber":27,"sourceCode":"\nexport class FsSandboxError extends Error {\n  constructor(\n    public readonly code: string,\n    message: string\n  ) {\n    super(message)\n    this.name = 'FsSandboxError'\n  }\n}\n\nconst PATH_MAX = 4096\n\nexport async function resolveInsideSandbox(\n  root: string,\n  userPath: string\n): Promise<string> {\n  if (userPath.length > PATH_MAX) {\n    throw new FsSandboxError(\n      'plugin.fs.path_too_long',\n      `plugin.fs.path_too_long: path exceeds ${PATH_MAX} characters`\n    )\n  }\n  const normalized = userPath.normalize('NFC')\n  const absolute = path.resolve(root, normalized)\n  let real: string\n  try {\n    real = await realpath(absolute)\n  } catch (e: unknown) {\n    if ((e as NodeJS.ErrnoException).code === 'ENOENT') {\n      real = path.normalize(\n        path.join(\n          await realpath(path.dirname(absolute)),\n          path.basename(absolute)\n        )\n      )\n    } else {","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/agalwood/Motrix/blob/1a708ee57746c434e2c67a44bbf0906a976afea4/src/core/plugin/capabilities/fs-sandbox.ts#L9-L45","documentation":"Thrown by `resolveInsideSandbox(root, userPath)` when `userPath.length > PATH_MAX` (4096). The sandbox resolver rejects oversized paths up front before doing any normalization or filesystem work, as a hard guard against path-traversal payloads that rely on length. Code is `plugin.fs.path_too_long`.","triggerScenarios":"Calling any fs-storage operation (stat/read/write/delete) whose relPath, after concatenation by the caller, exceeds 4096 characters; or a plugin directly invoking resolveInsideSandbox with a path built from unbounded user input.","commonSituations":"Plugin constructs a path from a long list of segments without a cap; adversarial input designed to overflow path buffers; a deeply recursive directory generator producing very long relative paths.","solutions":["Cap relPath length at the plugin's trust boundary (e.g. reject >1024 chars well under the 4096 limit) and surface a friendlier error.","Switch from a single long relPath to multiple shorter operations or use directory hierarchy.","Sanitize/reject inputs that are concatenated into the path before calling the fs API.","If the long path is legitimate, restructure storage so individual operations stay under the cap."],"exampleFix":"// before\nconst rel = segments.join('/') // segments unbounded\nawait storage.read(rel) // may exceed 4096\n\n// after\nconst rel = segments.join('/')\nif (rel.length > 1024) {\n  throw new Error(`path too long: ${rel.length} chars`)\n}\nawait storage.read(rel)","handlingStrategy":"validation","validationCode":"function assertRelPathLen(rel: string, max = 1024): void {\n  if (typeof rel !== 'string' || rel.length > max) {\n    throw new Error(`relPath too long (${rel?.length} > ${max})`)\n  }\n}","typeGuard":"function isPathTooLong(e: unknown): boolean {\n  return e instanceof Error && (e as FsSandboxError).code === 'plugin.fs.path_too_long'\n}","tryCatchPattern":"try {\n  await storage.read(rel)\n} catch (e) {\n  if (isPathTooLong(e)) { /* reject caller's input */ }\n  else throw e\n}","preventionTips":["Bound path length at your trust boundary well under 4096.","Avoid building paths from unbounded lists of segments.","Hash/truncate long externally-derived names."],"tags":["fs","sandbox","path-validation","limits"],"backgroundTag":null,"analyzedSha":"1a708ee57746c434e2c67a44bbf0906a976afea4","analyzedAt":"2026-08-12T16:18:09.346Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}