actualbudget/actual · error · Error

Could not resolve on-disk budget id for syncId ${syncId} aft

Error message

Could not resolve on-disk budget id for syncId ${syncId} after download.

What it means

resolveBudgetIdForSyncId maps a sync/cloud identifier (groupId or cloudFileId) to a local on-disk budget id. After downloading the budget from the server it lists known budgets and looks for one whose groupId or cloudFileId matches the syncId. If no local budget file matches even after download, the mapping failed and this error is thrown.

Source

Thrown at packages/cli/src/connection.ts:33

type ConnectionOptions = {
  mutates: boolean;
  skipBudget?: boolean;
};

function info(message: string, verbose?: boolean) {
  if (verbose) process.stderr.write(message + '\n');
}

async function resolveBudgetIdForSyncId(syncId: string): Promise<string> {
  const budgets = await api.getBudgets();
  const match = budgets.find(
    b =>
      typeof b.id === 'string' &&
      (b.groupId === syncId || b.cloudFileId === syncId),
  );
  if (!match?.id) {
    throw new Error(
      `Could not resolve on-disk budget id for syncId ${syncId} after download.`,
    );
  }
  return match.id;
}

export async function withConnection<T>(
  globalOpts: CliGlobalOpts,
  fn: (config: CliConfig) => Promise<T>,
  { mutates, skipBudget = false }: ConnectionOptions,
): Promise<T> {
  const config = await resolveConfig(globalOpts);

  info(`Connecting to ${config.serverUrl}...`, globalOpts.verbose);

  if (config.sessionToken) {
    await api.init({
      serverURL: config.serverUrl,

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Verify the syncId against the server (budget settings / sync id) and correct the --sync-id value.
  2. Check --data-dir points to the directory that actually holds your budget files.
  3. Re-run the command — a transient download failure can leave no local file; a retry can fix it.
  4. List locally available budgets (budgets command) and use the correct id/group.

Example fix

// before
actual-cli accounts --sync-id 8f2c...wrong
// after
actual-cli accounts --sync-id 8f2c...correct-sync-id
Defensive patterns

Strategy: try-catch

Validate before calling

import { getBudgets } from '@actual-app/api';
const budgets = await getBudgets();
const match = budgets.find(b => b.groupId === syncId || b.cloudFileId === syncId);
if (!match) throw new Error(`syncId ${syncId} not present locally; check --data-dir or re-download.`);

Type guard

function resolvesToBudget(budgets: { id: unknown; groupId?: string; cloudFileId?: string }[], syncId: string): boolean {
  return budgets.some(b => typeof b.id === 'string' && (b.groupId === syncId || b.cloudFileId === syncId));
}

Try / catch

try {
  await withConnection(config, async () => { /* ... */ });
} catch (err) {
  if (err instanceof Error && err.message.startsWith('Could not resolve on-disk budget id')) {
    console.error(`Sync id ${syncId} has no local budget. Verify --sync-id and --data-dir, then retry.`);
    process.exit(1);
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling a command with --sync-id (or ACTUAL_SYNC_ID) whose value matches no budget in the data dir: wrong syncId, budget deleted locally after download failed, or dataDir pointing at the wrong directory.

Common situations: Copying a syncId from an old server export; pointing --data-dir at a fresh empty directory; budget deleted on another device before the local download completed; typo'd syncId.

Related errors


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