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
- Set the x-actual-file-id header to a single valid file id string on the download request.
- Ensure no proxy adds or duplicates the header; remove duplicate x-actual-file-id entries so express yields a string.
- 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
- Always set x-actual-file-id as a single header value on download requests.
- Check reverse-proxy config doesn't strip or duplicate custom X- headers.
- Centralize sync-server API calls in one client helper that injects the header.
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
- Invalid Origin header
- fileId-required
- invalid-file-id
- invalid-file-id
- Invalid --name: must be a non-empty string.
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/306a1d9648bc9971.
Report an issue: GitHub.