{"record":{"id":"18b6462bbf0f844c","repo":"windmill-labs/windmill","slug":"missing-root-folder","errorCode":null,"errorMessage":"Missing root folder","messagePattern":"Missing root folder","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/common/fileUpload/FileUpload.svelte","lineNumber":192,"sourceCode":"\t}\n\n\tlet uniqueFolderPrefix = getRandomFolderPrefix()\n\tlet uploadedFolderRoot: string | undefined = undefined\n\n\tasync function uploadFileToS3(fileToUpload: File & { path?: string }, fileToUploadKey: string) {\n\t\tif (fileToUpload === undefined || fileToUploadKey === undefined) {\n\t\t\treturn\n\t\t}\n\t\tlet path: string | undefined = undefined\n\t\tlet fileExtension: string | undefined = undefined\n\t\tif (folderOnly) {\n\t\t\tconst relativePath = fileToUpload.webkitRelativePath || fileToUpload.path\n\t\t\tif (!relativePath) {\n\t\t\t\tthrow new Error('Missing file relative path')\n\t\t\t}\n\t\t\tconst rootFolder = relativePath.split('/')[0]\n\t\t\tif (!rootFolder) {\n\t\t\t\tthrow new Error('Missing root folder')\n\t\t\t}\n\t\t\tif (uploadedFolderRoot && rootFolder !== uploadedFolderRoot) {\n\t\t\t\tthrow new Error('Uploading a file in a different root folder than the previous files')\n\t\t\t} else {\n\t\t\t\tuploadedFolderRoot = rootFolder\n\t\t\t}\n\t\t\tpath = uniqueFolderPrefix + relativePath\n\t\t} else if (randomFileKey) {\n\t\t\tfileExtension = fileToUpload.name.split('.').pop()\n\t\t\tif (emptyString(fileExtension)) {\n\t\t\t\tfileExtension = undefined\n\t\t\t}\n\t\t} else {\n\t\t\tpath =\n\t\t\t\ttypeof pathTransformer == 'function'\n\t\t\t\t\t? ((await pathTransformer?.({\n\t\t\t\t\t\t\tfile: fileToUpload\n\t\t\t\t\t\t})) ?? fileToUploadKey)","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/common/fileUpload/FileUpload.svelte#L174-L210","documentation":"In folder-only uploads, uploadFileToS3() derives the root folder as the first path segment of the relative path. If that segment is empty (e.g. relativePath starts with '/' or is just '/'), it throws 'Missing root folder'.","triggerScenarios":"A file whose webkitRelativePath/path has an empty first segment, so relativePath.split('/')[0] is ''.","commonSituations":"Manually crafted paths like '/sub/file.txt'; synthetic File objects with a leading-slash path; odd drag-and-drop entry paths.","solutions":["Ensure each file's relative path starts with the folder name (e.g. 'root/sub/file.txt'), not with '/'","Normalize the path before upload: strip leading slashes or prepend a root name","Re-select the folder with the directory picker to regenerate correct paths"],"exampleFix":"// before\nfile.path = '/root/a.txt' // root is ''\n// after\nfile.path = 'root/a.txt'","handlingStrategy":"validation","validationCode":"const rel = file.webkitRelativePath || file.path\nconst root = rel?.split('/')[0]\nif (folderOnly && !root) {\n  console.error('Relative path must start with a root folder name')\n}","typeGuard":"function hasRootFolder(f: File & {webkitRelativePath?: string; path?: string}): boolean {\n  const rel = f.webkitRelativePath || f.path || ''\n  return rel.split('/')[0] !== ''\n}","tryCatchPattern":"try {\n  await uploadFileToS3(file)\n} catch (e) {\n  if (e.message === 'Missing root folder') {\n    console.error('Normalize paths so the first segment is the root folder', e)\n  } else throw e\n}","preventionTips":["Strip leading slashes from synthetic paths","Prepend a root name when constructing paths programmatically","Use the native directory picker which always yields 'root/...' paths"],"tags":["file-upload","s3","path-validation"],"backgroundTag":"missing-upload-root-folder","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}