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

  1. Upgrade @actual-app/api and the server to matching versions.
  2. Verify a budget is loaded (e.g. call a budget query first) before exporting.
  3. Restart the API process after upgrades to clear stale state.
  4. 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

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


AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29). Data as JSON: /api/errors/431fa04395687863. Report an issue: GitHub.