{"record":{"id":"e5091ed9ad26d316","repo":"Mintplex-Labs/anything-llm","slug":"folder-name-cannot-contain-path-separators","errorCode":null,"errorMessage":"Folder name cannot contain path separators.","messagePattern":"Folder name cannot contain path separators\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"server/utils/files/index.js","lineNumber":720,"sourceCode":" * @returns {string} the normalized folder name the documents were moved into\n * @throws {Error} if the folder name is empty, escapes basePath, or is nested\n */\nfunction moveProcessedDocsToFolder(\n  documents = [],\n  folderName = \"\",\n  basePath = documentsPath\n) {\n  const folder = normalizePath(folderName);\n  if (!folder) throw new Error(\"Invalid folder name.\");\n\n  // Deliberate: document storage is exactly two segments (`folder/file.json`)\n  // and docpath, the embedding pipeline and the vector cache all assume that\n  // shape. A nested folder name would produce documents that the file picker\n  // (which only enumerates one level below documentsPath) cannot see and that\n  // cannot be embedded. /v1/document/upload/:folderName historically accepted\n  // a URL-encoded separator here; that is now rejected.\n  if (folder.includes(\"/\") || folder.includes(\"\\\\\"))\n    throw new Error(\"Folder name cannot contain path separators.\");\n\n  const targetFolderPath = path.join(basePath, folder);\n  if (!isWithin(path.resolve(basePath), path.resolve(targetFolderPath)))\n    throw new Error(\"Invalid folder name.\");\n  if (!fs.existsSync(targetFolderPath))\n    fs.mkdirSync(targetFolderPath, { recursive: true });\n\n  for (const doc of documents) {\n    const currentFolder = path.dirname(doc.location);\n    if (currentFolder === folder) continue;\n\n    const sourcePath = path.join(basePath, normalizePath(doc.location));\n    const destinationPath = path.join(\n      targetFolderPath,\n      path.basename(doc.location)\n    );\n\n    if (!isWithin(basePath, sourcePath) || !isWithin(basePath, destinationPath))","sourceCodeStart":702,"sourceCodeEnd":738,"githubUrl":"https://github.com/Mintplex-Labs/anything-llm/blob/526360e320da9d1b36074be5ed64fe76e5bbfbbd/server/utils/files/index.js#L702-L738","documentation":"Thrown by moveProcessedDocsToFolder() after normalization when the folder still contains a forward or back slash. AnythingLLM document storage is deliberately a flat two-segment layout (folder/file.json); the file picker, embedding pipeline, and vector cache all assume one level below documentsPath. A nested folder name would create invisible, un-embeddable documents, so separators are hard-rejected. The old /v1/document/upload/:folderName route that accepted URL-encoded separators is now blocked here.","triggerScenarios":"Calling moveProcessedDocsToFolder with a value like \"a/b\", \"sub/dir\", \"parent\\\\child\", or a URL-decoded \"%2F\". A client that tries to upload into a nested path via the folderName parameter.","commonSituations":"Migrating from an older AnythingLLM version where nested folders were tolerated; a third-party integration sending OS-style paths as the folder; a user typing \"projects/2024\" in the folder field.","solutions":["Send a single path segment as folderName (letters, digits, dash, underscore only).","If you need hierarchy, flatten it client-side into a unique slug (e.g. 'projects-2024') or use a workspace instead of a nested folder.","Strip or reject slashes at the API boundary before reaching the storage layer.","Update any client that historically URL-encoded a separator into :folderName."],"exampleFix":"// before\nmoveProcessedDocsToFolder(docs, 'my/sub/folder'); // throws\n\n// after\nconst slug = folderName.replace(/[^a-zA-Z0-9._-]/g, '-');\nmoveProcessedDocsToFolder(docs, slug);","handlingStrategy":"validation","validationCode":"if (folderName.includes('/') || folderName.includes('\\\\'))\n  return res.status(400).json({ error: 'Folder name cannot contain path separators.' });","typeGuard":"function isSingleSegment(v): v is string {\n  return typeof v === 'string' && !v.includes('/') && !v.includes('\\\\');\n}","tryCatchPattern":"try {\n  await moveProcessedDocsToFolder(docs, folderName);\n} catch (e) {\n  if (e.message.startsWith('Folder name cannot contain'))\n    return res.status(400).json({ error: e.message });\n  throw e;\n}","preventionTips":["Restrict folder names to /^[a-zA-Z0-9._-]+$/ at the input layer.","Flatten any hierarchy client-side into a slug before sending.","Document the single-segment contract in the upload API."],"tags":["validation","filesystem","document-storage","path-separator"],"backgroundTag":null,"analyzedSha":"526360e320da9d1b36074be5ed64fe76e5bbfbbd","analyzedAt":"2026-08-13T01:45:47.170Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}