{"record":{"id":"65e0c9421f47a12a","repo":"actualbudget/actual","slug":"fileid-required","errorCode":"fileId-required","errorMessage":"fileId-required","messagePattern":"fileId-required","errorType":"validation","errorClass":null,"httpStatus":422,"severity":"warning","filePath":"packages/sync-server/src/app-sync.ts","lineNumber":548,"sourceCode":"    data: {\n      deleted: boolToInt(file.deleted), //   FIXME: convert to boolean, make sure it works in the frontend\n      fileId: file.id,\n      groupId: file.groupId,\n      name: file.name,\n      encryptMeta: file.encryptMeta ? JSON.parse(file.encryptMeta) : null,\n      usersWithAccess: fileService.findUsersWithAccess(file.id).map(access => ({\n        ...access,\n        owner: access.userId === file.owner,\n      })),\n    },\n  });\n});\n\napp.post('/delete-user-file', (req, res) => {\n  const { fileId } = req.body || {};\n\n  if (!fileId) {\n    res.status(422).send({\n      details: 'fileId-required',\n      reason: 'unprocessable-entity',\n      status: 'error',\n    });\n    return;\n  }\n\n  const filesService = new FilesService(getAccountDb());\n  const file = verifyFileExists(fileId, filesService, res, 'file-not-found');\n  if (!file) {\n    return;\n  }\n\n  const fileAccessError = requireFileOwner(file, res.locals.user_id);\n  if (fileAccessError) {\n    res.status(403);\n    res.send(fileAccessError);\n    return;","sourceCodeStart":530,"sourceCodeEnd":566,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/sync-server/src/app-sync.ts#L530-L566","documentation":"POST /delete-user-file requires a fileId in the JSON body. When req.body is missing or has no truthy fileId, the server responds 422 with { details: 'fileId-required', reason: 'unprocessable-entity', status: 'error' }. This is a request-body validation error before any database or filesystem work.","triggerScenarios":"Calling /delete-user-file with an empty body, with Content-Type not set to application/json so req.body is {}, or with a body like {} / { fileId: null }.","commonSituations":"curl POSTs without -H 'Content-Type: application/json'; fetch calls omitting JSON.stringify of the payload; clients passing the file id under a wrong key (e.g. id instead of fileId).","solutions":["Send a JSON body containing fileId: POST /delete-user-file with { \"fileId\": \"<id>\" }.","Set Content-Type: application/json on the request so the express json parser populates req.body.","Confirm the key name is exactly fileId (not id or fileId2)."],"exampleFix":"// before\nawait fetch(base + '/delete-user-file', { method: 'POST' });\n// after\nawait fetch(base + '/delete-user-file', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ fileId }),\n});","handlingStrategy":"validation","validationCode":"if (!fileId || typeof fileId !== 'string') {\n  throw new Error('delete-user-file requires a non-empty fileId in the JSON body');\n}","typeGuard":"function hasFileId(body: unknown): body is { fileId: string } {\n  return typeof body === 'object' && body !== null &&\n    'fileId' in body && typeof (body as { fileId: unknown }).fileId === 'string' &&\n    (body as { fileId: string }).fileId.length > 0;\n}","tryCatchPattern":"const res = await fetch(base + '/delete-user-file', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ fileId }) });\nif (res.status === 422) throw new Error('fileId-required: body must be JSON with fileId');","preventionTips":["Always send JSON bodies with Content-Type: application/json.","Use a typed API client so the fileId parameter is enforced at compile time.","Double-check parameter naming matches the API (fileId, not id)."],"tags":["http","validation","request-body","client-error"],"backgroundTag":"missing-required-parameter","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}