{"record":{"id":"f09e690d67ec9def","repo":"hcengineering/platform","slug":"missing-files","errorCode":null,"errorMessage":"missing files","messagePattern":"missing files","errorType":"http","errorClass":null,"httpStatus":400,"severity":"warning","filePath":"services/datalake/pod-datalake/src/handlers/blob.ts","lineNumber":250,"sourceCode":"    res.status(204).send()\n  } catch (error: any) {\n    Analytics.handleError(error)\n    ctx.error('failed to delete blob', { workspace, name, error })\n    res.status(500).send()\n  }\n}\n\nexport async function handleUploadFormData (\n  ctx: MeasureContext,\n  req: Request,\n  res: Response,\n  datalake: Datalake,\n  tempDir: TemporaryDir\n): Promise<void> {\n  const workspace = req.params.workspace as WorkspaceUuid\n\n  if (req.files == null) {\n    res.status(400).send('missing files')\n    return\n  }\n\n  const files: [UploadedFile, key: string][] = []\n  Object.entries(req.files).forEach(([key, value]) => {\n    files.push([value as UploadedFile, key])\n  })\n\n  const result = await Promise.all(\n    files.map(async ([file, key]) => {\n      try {\n        const name = file.name\n        const size = file.size\n        const contentType = file.mimetype\n\n        let sha256: string\n        try {\n          sha256 =","sourceCodeStart":232,"sourceCodeEnd":268,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/services/datalake/pod-datalake/src/handlers/blob.ts#L232-L268","documentation":"handleUploadFormData requires multipart file uploads to carry at least one file. req.files is produced by the file-upload middleware; if it is null/undefined the handler responds 400 'missing files'. It prevents empty uploads from creating phantom blob entries.","triggerScenarios":"POSTing multipart/form-data to the upload endpoint with no file parts, sending fields only, or using a Content-Type other than multipart/form-data so the middleware never populates req.files.","commonSituations":"curl uploads without -F (using -d instead); clients setting the wrong Content-Type manually; middleware file-size/field limits silently dropping the file parts.","solutions":["Send the request as multipart/form-data with at least one file part (e.g. curl -F 'name=@file').","Do not set Content-Type manually; let the HTTP client add the multipart boundary.","Check that MaxFileSize limits in server config are not rejecting the file before req.files is populated."],"exampleFix":"// before\ncurl -X POST $URL -H 'Content-Type: application/json' -d '{\"name\":\"f\"}'\n// after\ncurl -X POST $URL -F 'name=@./file.bin'","handlingStrategy":"validation","validationCode":"if (!files || files.length === 0) {\n  throw new Error('at least one file must be selected for upload')\n}","typeGuard":"function hasFiles(b: unknown): b is Record<string, File> {\n  return !!b && typeof b === 'object' && Object.keys(b as object).length > 0\n}","tryCatchPattern":"const res = await fetch(url, { method: 'POST', body: form })\nif (res.status === 400 && (await res.text()).includes('missing files')) {\n  throw new Error('no file parts were attached to the multipart request')\n}","preventionTips":["Use FormData and let the HTTP client set the multipart Content-Type.","Check the file list is non-empty before issuing the request.","Ensure upload middleware limits do not silently drop the file parts."],"tags":["http","upload","multipart","validation"],"backgroundTag":"missing-file-upload","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}