actualbudget/actual · warning

fileId-required

fileId-required

Error message

fileId-required

What it means

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.

Source

Thrown at packages/sync-server/src/app-sync.ts:548

    data: {
      deleted: boolToInt(file.deleted), //   FIXME: convert to boolean, make sure it works in the frontend
      fileId: file.id,
      groupId: file.groupId,
      name: file.name,
      encryptMeta: file.encryptMeta ? JSON.parse(file.encryptMeta) : null,
      usersWithAccess: fileService.findUsersWithAccess(file.id).map(access => ({
        ...access,
        owner: access.userId === file.owner,
      })),
    },
  });
});

app.post('/delete-user-file', (req, res) => {
  const { fileId } = req.body || {};

  if (!fileId) {
    res.status(422).send({
      details: 'fileId-required',
      reason: 'unprocessable-entity',
      status: 'error',
    });
    return;
  }

  const filesService = new FilesService(getAccountDb());
  const file = verifyFileExists(fileId, filesService, res, 'file-not-found');
  if (!file) {
    return;
  }

  const fileAccessError = requireFileOwner(file, res.locals.user_id);
  if (fileAccessError) {
    res.status(403);
    res.send(fileAccessError);
    return;

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Send a JSON body containing fileId: POST /delete-user-file with { "fileId": "<id>" }.
  2. Set Content-Type: application/json on the request so the express json parser populates req.body.
  3. Confirm the key name is exactly fileId (not id or fileId2).

Example fix

// before
await fetch(base + '/delete-user-file', { method: 'POST' });
// after
await fetch(base + '/delete-user-file', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ fileId }),
});
Defensive patterns

Strategy: validation

Validate before calling

if (!fileId || typeof fileId !== 'string') {
  throw new Error('delete-user-file requires a non-empty fileId in the JSON body');
}

Type guard

function hasFileId(body: unknown): body is { fileId: string } {
  return typeof body === 'object' && body !== null &&
    'fileId' in body && typeof (body as { fileId: unknown }).fileId === 'string' &&
    (body as { fileId: string }).fileId.length > 0;
}

Try / catch

const res = await fetch(base + '/delete-user-file', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ fileId }) });
if (res.status === 422) throw new Error('fileId-required: body must be JSON with fileId');

Prevention

When it happens

Trigger: 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 }.

Common situations: 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).

Understand the failure class

Background: Missing required parameter errors: what 'X is required' and 'the required X param is missing' mean, and how to fix them — this error's family across 27 libraries.

Related errors


AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29). Data as JSON: /api/errors/65e0c9421f47a12a. Report an issue: GitHub.