actualbudget/actual · error · FileDownloadError
invalid-meta-file
invalid-meta-file
Error message
invalid-meta-file
What it means
FileDownloadError('invalid-meta-file') is thrown by importBuffer when metadata.json inside the downloaded archive cannot be parsed as JSON. The db.sqlite file was found, but the accompanying metadata (budget id, name, cloud ids) is corrupt, so the budget cannot be registered locally.
Source
Thrown at packages/loot-core/src/server/cloud-storage.ts:253
: sharedDirs.length === 1
? sharedDirs[0]
: null;
if (dir == null) {
throw FileDownloadError('invalid-zip-file');
}
const entryName = dir + 'db.sqlite';
const metaEntryName = dir + 'metadata.json';
const dbContent = Buffer.from(entries[entryName]);
const metaContent = Buffer.from(entries[metaEntryName]);
let meta;
try {
meta = JSON.parse(metaContent.toString('utf8'));
} catch {
throw FileDownloadError('invalid-meta-file');
}
// Update the metadata. The stored file on the server might be
// out-of-date with a few keys
meta = {
...meta,
cloudFileId: fileData.fileId,
groupId: fileData.groupId,
lastUploaded: monthUtils.currentDay(),
encryptKeyId: fileData.encryptMeta ? fileData.encryptMeta.keyId : null,
};
const budgetDir = fs.getBudgetDir(meta.id);
if (await fs.exists(budgetDir)) {
// Don't remove the directory so that backups are retained
const dbFile = fs.join(budgetDir, 'db.sqlite');
const metaFile = fs.join(budgetDir, 'metadata.json');View on GitHub (pinned to d4334cb6e6)
Solutions
- Re-download the file from the server; the local copy may be corrupted in transit
- Restore the budget from a local backup (files/backups directory) and re-upload it
- If the metadata is recoverable, fix metadata.json to be valid JSON with at least an "id" field, then re-zip
- Check sync-server logs/storage for corruption and re-upload a known-good export
Example fix
// before: trusting archive metadata
await importActual(buffer);
// after: pre-validate metadata JSON
const meta = JSON.parse(zip.entries['metadata.json'].toString('utf8')); // throws early with clearer error
if (!meta.id) throw new Error('metadata.json missing id'); Defensive patterns
Strategy: validation
Validate before calling
function validateArchiveMeta(entries) {
const entry = Object.keys(entries).find(n => n === 'metadata.json' || n.endsWith('/metadata.json'));
if (!entry) throw new Error('metadata.json missing');
const meta = JSON.parse(Buffer.from(entries[entry]).toString('utf8'));
if (typeof meta.id !== 'string' || !meta.id) throw new Error('metadata.json has no id');
return meta;
} Type guard
function isValidBudgetMeta(v) {
return typeof v === 'object' && v !== null && typeof v.id === 'string' && v.id.length > 0;
} Try / catch
try {
await importActual(buffer);
} catch (e) {
if (e instanceof FileDownloadError && e.reason === 'invalid-meta-file') {
console.error('metadata.json is corrupt; restore from backup or re-download.');
} else throw e;
} Prevention
- Do not edit metadata.json inside archives; if you must, validate JSON afterwards
- Verify cloud files periodically by downloading and parsing metadata
- Keep local backups in files/ so a corrupt cloud copy is recoverable
- Use binary-safe tools when handling zips (no text-mode conversions)
When it happens
Trigger: calling download() or importActual() with an archive whose metadata.json entry is empty, truncated, or contains invalid JSON.
Common situations: a partially uploaded/corrupted cloud file, archives edited by hand where metadata.json was mangled, disk corruption on the sync server, or encoding damage from binary-mode text edits.
Related errors
- invalid-zip-file
- Query file must contain a JSON object
- Invalid config file: expected an object with keys: ${configF
- Invalid catalog format: expected an array
- Transfer payee with account ID ${id} not found.
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/0a68b1d37d7298e2.
Report an issue: GitHub.