actualbudget/actual · error · Error
Sync ID is required for sync ${flag}. Set --sync-id or ACTUA
Error message
Sync ID is required for sync ${flag}. Set --sync-id or ACTUAL_SYNC_ID. What it means
The `actual sync <flag>` commands (e.g. --download or --upload) synchronize a specific budget identified by a sync ID. requireSyncIdAndMeta resolves the CLI config and throws when neither the --sync-id flag nor the ACTUAL_SYNC_ID environment variable provides one. No sync target can be determined, so the operation aborts before connecting.
Source
Thrown at packages/cli/src/commands/sync.ts:24
import { CACHE_FILE_NAME, getMetaDir, readCacheState } from '#cache';
import type { CliConfig } from '#config';
import { resolveConfig } from '#config';
import { withConnection } from '#connection';
import { acquireExclusive } from '#lock';
import { printOutput } from '#output';
type SyncCmdOpts = {
status?: boolean;
clear?: boolean;
};
async function requireSyncIdAndMeta(
opts: Record<string, unknown>,
flag: string,
): Promise<{ config: CliConfig; meta: string }> {
const config = await resolveConfig(opts);
if (!config.syncId) {
throw new Error(
`Sync ID is required for sync ${flag}. Set --sync-id or ACTUAL_SYNC_ID.`,
);
}
return { config, meta: getMetaDir(config.dataDir, config.syncId) };
}
export function registerSyncCommand(program: Command) {
program
.command('sync')
.description(
'Sync the local cached budget with the server, print cache status, or clear the cache',
)
.option('--status', 'Print cache status without syncing', false)
.option(
'--clear',
'Delete the local cache; next command re-downloads',
false,
)View on GitHub (pinned to d4334cb6e6)
Solutions
- Pass the budget's sync ID explicitly: `actual sync --download --sync-id <id>`
- Export ACTUAL_SYNC_ID=<id> in your shell profile or CI environment
- Find the sync ID from the budget file or server (the ID of the budget you downloaded with `actual download`)
- Set syncId in the CLI config file so all commands inherit it
Example fix
// before actual sync --download // after export ACTUAL_SYNC_ID="a1b2c3d4-..." actual sync --download # or actual sync --download --sync-id "a1b2c3d4-..."
Defensive patterns
Strategy: validation
Validate before calling
const syncId = process.env.ACTUAL_SYNC_ID ?? flags.syncId;
if (!syncId) {
throw new Error('Set ACTUAL_SYNC_ID or pass --sync-id before running `actual sync`');
} Type guard
function hasSyncTarget(o: { syncId?: string }): o is { syncId: string } {
return typeof o.syncId === 'string' && o.syncId.length > 0;
} Try / catch
try {
await run(['actual', 'sync', '--download']);
} catch (e) {
if (String(e.message).includes('Sync ID is required')) {
console.error('Provide --sync-id <id> or export ACTUAL_SYNC_ID=<id>');
} else throw e;
} Prevention
- Set ACTUAL_SYNC_ID in CI secrets and shell profiles, not just the interactive shell
- Record the budget's sync ID when you first `actual download` it
- Put syncId in the CLI config file for machine-level defaults
When it happens
Trigger: Running `actual sync --download` without --sync-id and with ACTUAL_SYNC_ID unset in the environment; ACTUAL_SYNC_ID set only in an interactive shell profile but the command runs from cron/CI; a config file that omits the syncId key.
Common situations: CI pipelines missing env vars; running sync on a machine where the budget was never downloaded; forgetting that sync IDs are per-budget and changing machines.
Related errors
- No fields to update. Use --name to specify a new name.
- No valid payee IDs provided in --ids. Provide comma-separate
- --table is required (or use --file or --last)
- At least one of --tag, --color, or --description is required
- Could not resolve on-disk budget id for syncId ${syncId} aft
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/9794aed8719cdedd.
Report an issue: GitHub.