actualbudget/actual · error
key-make must be called with file loaded
Error message
key-make must be called with file loaded
What it means
keyMake lets a user create or change the budget's encryption key, but only when a budget file is loaded — prefs.getPrefs() returns null otherwise. Without a loaded file there is no place to store the key metadata (salt, id), so the call is rejected with 'key-make must be called with file loaded'.
Source
Thrown at packages/loot-core/src/server/encryption/app.ts:29
import * as encryption from '.';
export type EncryptionHandlers = {
'key-make': typeof keyMake;
'key-test': typeof keyTest;
};
export const app = createApp<EncryptionHandlers>();
app.method('key-make', keyMake);
app.method('key-test', keyTest);
// A user can only enable/change their key with the file loaded. This
// will change in the future: during onboarding the user should be
// able to enable encryption. (Imagine if they are importing data from
// another source, they should be able to encrypt first)
async function keyMake({ password }: { password: string }) {
if (!prefs.getPrefs()) {
throw new Error('key-make must be called with file loaded');
}
const salt = encryption.randomBytes(32).toString('base64');
const id = uuidv4();
const key = await encryption.createKey({ id, password, salt });
// Load the key
await encryption.loadKey(key);
// Make some test data to use if the key is valid or not
const testContent = await makeTestMessage(key.getId());
// Changing your key necessitates a sync reset as well. This will
// clear all existing encrypted data from the server so you won't
// have a mix of data encrypted with different keys.
return await resetSync({
key,
salt,View on GitHub (pinned to d4334cb6e6)
Solutions
- Load a budget (loadBudget / createBudget) before calling key-make
- Defer encryption setup until after the file is opened in your app flow
- Check prefs.getPrefs() before invoking and show 'open a budget first' to the user
- Wait for the app's 'file loaded' / ready event before enabling key management calls
Example fix
// before
await send('key-make', { password });
// after
if (!prefs.getPrefs()) {
await send('load-budget', { id: budgetId });
}
await send('key-make', { password }); Defensive patterns
Strategy: validation
Validate before calling
import { prefs } from '../server/prefs';
if (!prefs.getPrefs()) {
throw new Error('Load a budget file before configuring encryption');
} Type guard
function isFileLoaded(getPrefs: () => unknown): boolean {
return getPrefs() != null;
} Try / catch
try {
await send('key-make', { password });
} catch (e) {
if (e.message.includes('must be called with file loaded')) {
await openBudgetFirst();
} else throw e;
} Prevention
- Gate all key-management calls behind a 'budget loaded' state in your app flow
- Listen for the file-loaded/ready event before enabling encryption UI
- In scripts, call loadBudget/createBudget before key-make
- Never assume prefs exist on a fresh install
When it happens
Trigger: Calling the key-make IPC/handler (keyMake({ password })) before any budget is opened — e.g. on a fresh install, after closing all budgets, or during onboarding before loadBudget finishes.
Common situations: Automation scripts invoking encryption setup at startup before a budget loads; testing the encryption API without loading a fixture budget; trying to pre-configure encryption before choosing/creating a file.
Related errors
- missing-key
- Budget file ID is required.
- Account with ID ${accountId} does not exist.
- This function cannot track error data. It needs to accept th
- No sync server configured.
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/b8931eae78de2d96.
Report an issue: GitHub.