actualbudget/actual · error · Error
Error exporting budget: no data was returned
Error message
Error exporting budget: no data was returned
What it means
If the export command neither errors nor includes data, the API throws rather than returning an empty buffer. This is a consistency guard: an export response without a payload is treated as a failure.
Source
Thrown at packages/api/methods.ts:94
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);
}
function toArrayBuffer(data: ArrayBuffer | Uint8Array): ArrayBuffer {
if (data instanceof Uint8Array) {
// Copy into a fresh ArrayBuffer so that views into a larger (possibly
// shared) buffer are not sent across the worker boundary as-is.
const copy = new Uint8Array(data.byteLength);
copy.set(data);
return copy.buffer;
}
return data;
}
export async function sync() {
return send('api/sync');
}View on GitHub (pinned to d4334cb6e6)
Solutions
- Upgrade @actual-app/api and the server to matching versions.
- Verify a budget is loaded (e.g. call a budget query first) before exporting.
- Restart the API process after upgrades to clear stale state.
- Report upstream with versions if it reproduces on a loaded budget.
Example fix
// before
const zip = await api.exportBudget();
// after
await api.downloadBudget(budgetId); // ensure budget loaded
const zip = await api.exportBudget();
if (!zip || zip.byteLength === 0) throw new Error('Export produced empty archive'); Defensive patterns
Strategy: validation
Validate before calling
const zip = await api.exportBudget();
if (!(zip instanceof Uint8Array) || zip.byteLength === 0) {
throw new Error('Export returned no usable data');
} Type guard
function isNonEmptyBytes(v: unknown): v is Uint8Array {
return v instanceof Uint8Array && v.byteLength > 0;
} Try / catch
let zip: Uint8Array;
try {
const out = await api.exportBudget();
if (!isNonEmptyBytes(out)) throw new Error('no data was returned');
zip = out;
} catch (e) {
console.error('Export unavailable:', e.message);
await api.downloadBudget(id); // reload then retry
zip = await api.exportBudget();
} Prevention
- Verify budget is loaded before export (successful query first).
- Check exported zip byte length and PK header before persisting.
- Restart processes after version upgrades.
- Pin matching versions of @actual-app/api and sync-server.
When it happens
Trigger: The core 'export-budget' handler resolves with an unexpected/empty response — usually from a version mismatch between @actual-app/api and the running core/server, or an export handler path that returns nothing.
Common situations: Mixing versions of @actual-app/api and sync-server in a deployment; calling exportBudget before any budget is actually loaded; a stale long-running process after an upgrade.
Related errors
- Error importing budget: no budget was loaded
- Error exporting budget: ${result.error}
- Error importing budget: ${result.error}
- Amount to hold needs to be greater than 0
- @actual-app/api requires a node version ${minimumNodeVersion
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/431fa04395687863.
Report an issue: GitHub.