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
- Verify the syncId against the server (budget settings / sync id) and correct the --sync-id value.
- Check --data-dir points to the directory that actually holds your budget files.
- Re-run the command — a transient download failure can leave no local file; a retry can fix it.
- 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
- Copy the sync id directly from the budget's settings page instead of typing it.
- Pin --data-dir to the directory used by previous successful runs.
- Retry once on this error — the preceding download may have failed transiently.
- List budgets on disk after failures to see what actually downloaded.
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
- Sync ID is required for sync ${flag}. Set --sync-id or ACTUA
- Sync ID is required for this command. Set --sync-id or ACTUA
- No sync server configured.
- Error importing budget: ${result.error}
- Error importing budget: no budget was loaded
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/b489422b4d6c2f17.
Report an issue: GitHub.