actualbudget/actual · error
invalid fileId
Error message
invalid fileId
What it means
verifyFileExists validates the fileId supplied to file-scoped sync routes (e.g. /sync/file, /sync/currentfile). If it is not a string or fails isValidFileId, the route returns 400 with body 'invalid fileId'.
Source
Thrown at packages/sync-server/src/app-sync.ts:101
const value = req.headers[key];
if (!value) {
return null;
}
if (typeof value !== 'string') {
res.status(400).send('Duplicate headers encountered for key ' + key);
return null;
}
return value;
}
const verifyFileExists = (
fileId: unknown,
filesService: FilesService,
res: Response,
errorObject: string | Record<string, unknown>,
) => {
if (typeof fileId !== 'string' || !isValidFileId(fileId)) {
res.status(400).send('invalid fileId');
return;
}
try {
return filesService.get(fileId);
} catch (e) {
if (e instanceof FileNotFound) {
//FIXME: error code should be 404. Need to make sure frontend is ok with it.
//TODO: put this into a middleware that checks if FileNotFound is thrown and returns 404 and same error message
// for every FileNotFound error
res.status(400).send(errorObject);
return;
}
throw e;
}
};
function requireFileOwner(file: File, userId: string) {View on GitHub (pinned to d4334cb6e6)
Solutions
- Pass a valid file id string, obtained from the files list endpoint for the authenticated user.
- URL-encode the id properly when placing it in the query/path.
- Re-download the list of budgets and use the current id if the file was recreated.
Example fix
// before curl "$SERVER/sync/file?fileId=abc" // after: use a real file id FILE_ID=$(curl -s -H "$AUTH" "$SERVER/files" | jq -r '.data[0].fileId') curl -H "$AUTH" "$SERVER/sync/file?fileId=$FILE_ID"
Defensive patterns
Strategy: validation
Validate before calling
const uuidRe = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
if (typeof fileId !== 'string' || !uuidRe.test(fileId)) throw new Error('pass a valid file id string to the sync file endpoints'); Type guard
const isFileId = (v) => typeof v === 'string' && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(v); Try / catch
try {
const res = await fetch(`${serverUrl}/sync/file?fileId=${fileId}`, { headers: authHeaders });
if (res.status === 400 && (await res.text()) === 'invalid fileId') throw new Error('fileId malformed; fetch a real id from /files');
return res;
} catch (e) { throw e; } Prevention
- Source file ids from the /files listing, never literals
- URL-encode ids placed in query strings
- Distinguish file id vs group id in scripts
When it happens
Trigger: Calling GET /sync/file or /sync/currentfile with a missing, empty, or malformed fileId (wrong format, URL-encoded incorrectly, numeric id).
Common situations: Stale ids saved in scripts after the budget was deleted and re-created; hand-built curl calls with a placeholder id; passing the group id instead of the file id.
Related errors
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/4a35f9cf8368d151.
Report an issue: GitHub.