actualbudget/actual · error · Error
Error importing budget: ${result.error}
Error message
Error importing budget: ${result.error} What it means
importBudget sends the 'import-budget' command to the core with the uploaded budget file. The core responds with an error field when the budget file cannot be imported (unparseable zip, wrong format, migration failure, etc.). The API surfaces that server-side message verbatim so the caller knows the import was rejected.
Source
Thrown at packages/api/methods.ts:78
*/
export async function importBudget(
input: string | ArrayBuffer | Uint8Array,
{
type = 'actual',
filename,
}: { type?: ImportableBudgetType; filename?: string } = {},
): Promise<{ id: string }> {
const result =
typeof input === 'string'
? await send('import-budget', { filepath: input, type })
: await send('import-budget', {
buffer: toArrayBuffer(input),
filename,
type,
});
if (result.error) {
throw new Error(`Error importing budget: ${result.error}`);
}
if (!result.id) {
throw new Error('Error importing budget: no budget was loaded');
}
return { id: result.id };
}
/** Export the currently-loaded budget as a zip buffer. */
export async function exportBudget(): Promise<Uint8Array> {
const result = await send('export-budget');
if ('error' in result) {
throw new Error(`Error exporting budget: ${result.error}`);
}
if (!result.data) {
throw new Error('Error exporting budget: no data was returned');
}
return new Uint8Array(result.data);View on GitHub (pinned to d4334cb6e6)
Solutions
- Open the file locally (File > Open budget) to confirm it is a valid Actual budget before importing programmatically.
- Re-export the budget from a matching Actual version and retry.
- Check the error detail embedded in the message for a concrete cause (invalid zip, migration error, etc.).
- Ensure the file is passed fully as ArrayBuffer/Uint8Array, not as a string or partially-read stream.
Example fix
// before
await api.importBudget(csvBuffer, 'budget.csv', 'text/csv');
// after
const res = await fetch(budgetUrl);
const buf = new Uint8Array(await res.arrayBuffer());
if (!budgetUrl.endsWith('.zip')) throw new Error('Not an Actual budget file');
await api.importBudget(buf, 'budget.zip', 'application/zip'); Defensive patterns
Strategy: validation
Validate before calling
function isPlausibleBudgetFile(buf: Uint8Array, name: string): boolean {
const isZip = buf.length > 4 && buf[0] === 0x50 && buf[1] === 0x4b && buf[2] === 0x03 && buf[3] === 0x04;
return isZip && name.endsWith('.zip') && buf.length > 1024;
}
if (!isPlausibleBudgetFile(buffer, filename)) throw new Error('Refusing to import: not a budget zip'); Type guard
function isImportFailure(r: { error?: string; id?: string }): r is { error: string } {
return typeof r.error === 'string' && r.error.length > 0;
} Try / catch
try {
const { id } = await api.importBudget(buffer, 'budget.zip', 'application/zip');
} catch (e) {
if (String(e.message).startsWith('Error importing budget:')) {
console.error('Budget import rejected:', e.message);
// surface to user / retry with a valid export
} else throw e;
} Prevention
- Only import .zip files produced by Actual's export/open.
- Verify the zip opens locally before scripted imports.
- Transfer files in binary mode; check byte length after download.
- Keep @actual-app/api and the server on matching versions.
When it happens
Trigger: Calling api.importBudget(buffer, filename, type) with a file that is not a valid Actual budget zip (e.g. an exported .zip from an incompatible version, a truncated/corrupted download, or a random file renamed to .zip).
Common situations: Automated migration scripts importing a budget exported from another instance; uploading an encrypted or password-protected zip; feeding a CSV/JSON file instead of an Actual budget file; a partially-uploaded file over an unstable network.
Related errors
- Error importing budget: no budget was loaded
- Error exporting budget: ${result.error}
- Error exporting budget: no data was returned
- zipMeta ? getUnsafeZipError(zipMeta) : error
- File does not appear to be a valid qif file: ${line}
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/6f8653873a553fd9.
Report an issue: GitHub.