actualbudget/actual · warning

Single file ID is required

Error message

Single file ID is required

What it means

GET /download-user-file requires the file id in the x-actual-file-id header. The server sends this 400 with the plain-text body 'Single file ID is required' when the header is absent or its value is not a string (per express header typing, an array). It is a request-shape validation error.

Source

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

  // Regardless, update some properties
  filesService.update(
    fileId,
    new FileUpdate({
      syncVersion: syncFormatVersion,
      encryptMeta,
      name,
    }),
  );

  res.send({ status: 'ok', groupId });
});

app.get('/download-user-file', async (req, res) => {
  const fileId = req.headers['x-actual-file-id'];
  if (typeof fileId !== 'string') {
    // FIXME: Not sure how this cannot be a string when the header is
    // set.
    res.status(400).send('Single file ID is required');
    return;
  }
  if (!isValidFileId(fileId)) {
    res.status(400).send('invalid fileId');
    return;
  }

  const filesService = new FilesService(getAccountDb());
  const file = verifyFileExists(
    fileId,
    filesService,
    res,
    'User or file not found',
  );

  if (!file) {
    return;
  }

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Set the x-actual-file-id header to a single valid file id string on the download request.
  2. Ensure no proxy adds or duplicates the header; remove duplicate x-actual-file-id entries so express yields a string.
  3. Verify with curl -H "x-actual-file-id: <fileId>" that the header survives your proxy chain.

Example fix

// before
await fetch(base + '/download-user-file');
// after
await fetch(base + '/download-user-file', {
  headers: { 'x-actual-file-id': fileId },
});
Defensive patterns

Strategy: validation

Validate before calling

if (typeof fileId !== 'string' || fileId.length === 0) {
  throw new Error('x-actual-file-id header required');
}

Type guard

function hasFileIdHeader(v: unknown): v is string {
  return typeof v === 'string' && v.length > 0;
}

Try / catch

const res = await fetch(url, { headers: { 'x-actual-file-id': fileId } });
if (res.status === 400) throw new Error('check x-actual-file-id header is set to a single string');

Prevention

When it happens

Trigger: Calling GET /download-user-file without setting the x-actual-file-id header, or in rare setups where the header appears multiple times so express parses it as an array instead of a string.

Common situations: Scripts or reverse proxies stripping custom X- headers; clients forgetting the header entirely; duplicate headers from misconfigured proxy config (headers: 'string' vs array in express types).

Related errors


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