actualbudget/actual · error
Could not find file: ${path}
Error message
Could not find file: ${path} What it means
When importing a YNAB4 (.ynab4) budget zip, parseFile unzips the archive and getFile(entries, path) looks up an entry whose name exactly equals the requested path. If no zip entry matches exactly, this error is thrown. It almost always means the archive has a different internal directory layout than the importer expects (e.g. an extra or missing root folder like 'Budget~.../*.ynab4/').
Source
Thrown at packages/loot-core/src/server/importers/ynab4.ts:420
return null;
}
unixFilepath = unixFilepath.replace(/\.zip$/, '').replace(/.ynab4$/, '');
// Most budgets are named like "Budget~51938D82.ynab4" but sometimes
// they are only "Budget.ynab4". We only want to grab the name
// before the ~ if it exists.
const m = unixFilepath.match(/([^/~]+)[^/]*$/);
if (!m) {
return null;
}
return m[1];
}
function getFile(entries: string[], path: string) {
const files = entries.filter(e => e === path);
if (files.length === 0) {
throw new Error('Could not find file: ' + path);
}
if (files.length >= 2) {
throw new Error('File name matches multiple files: ' + path);
}
return files[0];
}
function join(...paths: string[]): string {
return paths.slice(1).reduce(
(full, path) => {
return full + '/' + path.replace(/^\//, '');
},
paths[0].replace(/\/$/, ''),
);
}
export function parseFile(buffer: Buffer): YNAB4.YFull {
let zipped: Record<string, Uint8Array>;View on GitHub (pinned to d4334cb6e6)
Solutions
- Re-zip the budget so the archive contains the original structure: a top-level folder ending in .ynab4 containing Budget.yfull and the data files, with no extra wrapper folders.
- Inspect the zip contents (unzip -l budget.ynab4) and confirm Budget.yfull exists at the expected path; fix casing/nesting to match exactly.
- Verify you are importing a YNAB4 export, not a YNAB5 (.yfull vs nYab format); use the correct importer for the format.
- Extract the zip, confirm the file exists and is not corrupted, then re-zip without compression changes to the inner structure.
Example fix
// before (broken zip layout) Budget.yfull Data/payees.yfull // after (expected YNAB4 layout) MyBudget~9C1A.ynab4/Budget.yfull MyBudget~9C1A.ynab4/Data/payees.yfull
Defensive patterns
Strategy: validation
Validate before calling
import { unzipSync } from 'fflate';
function canParseYnab4(buffer) {
try {
const entries = Object.keys(safeUnzip(buffer));
return entries.some(e => /[^/]*\.ynab4/.test(e)) &&
entries.some(e => e.endsWith('Budget.yfull'));
} catch { return false; }
} Type guard
function isYnab4Zip(buffer) {
return Buffer.isBuffer(buffer) &&
buffer.length > 4 &&
buffer.subarray(0, 2).toString('latin1') === 'PK';
} Try / catch
try {
const budget = parseFile(buffer);
} catch (e) {
if (e.message.startsWith('Could not find file')) {
// inspect zip layout, re-export/re-zip the budget
} else throw e;
} Prevention
- Import only archives exported directly by YNAB4, never re-zipped folders.
- Run unzip -l budget.ynab4 and confirm a top-level *.ynab4 folder containing Budget.yfull before importing.
- Check for extra wrapper folders added by OS zip tools and remove them.
- Use the YNAB5 importer for nYab exports; do not feed them to the YNAB4 path.
When it happens
Trigger: Calling parseFile(buffer) on a YNAB4 zip whose entries, after root-directory stripping, do not contain an entry exactly equal to the computed yfullPath or other requested file (Budget.yfull, payees, etc.), e.g. because the zip lacks the top-level *.ynab4 directory or uses differing case/extra nesting.
Common situations: Re-exported or manually re-zipped YNAB4 backups where the folder structure changed; macOS/Windows zip tools adding an extra wrapper folder; renamed budget directory inside the archive; importing a YNAB5 (nYab) file through the YNAB4 importer path.
Related errors
- Error reading Budget.yfull file
- File name matches multiple files: ${path}
- Error reading zip file
- zipMeta ? getUnsafeZipError(zipMeta) : error
- File not found at the provided path: ${filepath}
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/6fb20d2ff8907573.
Report an issue: GitHub.