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

  1. Pass a valid file id string, obtained from the files list endpoint for the authenticated user.
  2. URL-encode the id properly when placing it in the query/path.
  3. 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

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.