actualbudget/actual · error
File does not appear to be a valid qif file
Error message
File does not appear to be a valid qif file: ${line} What it means
qif2json requires the first line of a QIF file to be a type header of the form '!Type:Bank' (or similar). If the first line is empty or does not match the expected type pattern, the parser cannot determine the account type and throws this error including the offending line.
Solutions
- Open the file and verify the very first non-empty line starts with '!Type:' (e.g. '!Type:Bank').
- Strip any UTF-8 BOM and leading blank lines before parsing.
- Confirm the file is actually QIF format, not CSV/OFX renamed to .qif; re-export from the bank in QIF format.
- Trim leading whitespace/newlines from the file content before passing it to parseQIF.
Example fix
// before
const data = qif2json.parse(rawFileContents);
// after
const cleaned = rawFileContents.replace(/^\uFEFF/, '').replace(/^\s+/, '');
if (!/^!Type:/im.test(cleaned.split(/\r?\n/, 1)[0])) {
throw new Error('Missing !Type header — not a valid QIF file');
}
const data = qif2json.parse(cleaned); Defensive patterns
Strategy: validation
Validate before calling
const firstLine = content.replace(/^\uFEFF/, '').split(/\r?\n/, 1)[0];
if (!/^!Type:/i.test(firstLine.trim())) {
throw new Error('Not a QIF file: missing !Type header on first line');
} Type guard
function isQifContent(content: string): boolean {
const first = content.replace(/^\uFEFF/, '').trimStart().split(/\r?\n/, 1)[0];
return /^!Type:/i.test(first);
} Try / catch
try {
const data = parseQIF(file);
} catch (e) {
if (e.message.startsWith('File does not appear to be a valid qif file')) {
showError('This file is not a valid QIF export. Check the first line is !Type:...');
} else { throw e; }
} Prevention
- Strip BOM and leading whitespace before parsing
- Verify the first line matches /^!Type:/i before importing
- Confirm the file extension and content both indicate QIF, not CSV/OFX
When it happens
Trigger: Importing a file whose first line is blank, whose '!Type' header is misspelled (e.g. '!TYPE:Bank' with wrong capitalization is fine but '!Type' missing entirely is not), a file exported with a BOM or leading whitespace, or passing a non-QIF file (CSV, OFX) to parseQIF.
Common situations: Bank exports that prepend metadata lines before the !Type header; files saved with UTF-8 BOM by Windows tools; users renaming a CSV to .qif; truncated downloads missing the header.
Related errors
- Unknown Detail Code
- Error importing budget
- An error occurred while parsing the template
- Could not find account for transaction when importing
- Could not find file
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/9e884fbd9dd21c70.
Report an issue: GitHub.
Appendix: source
Thrown at packages/loot-core/src/server/transactions/import/qif2json.ts:38
};
export function qif2json(qif, options: { dateFormat?: string } = {}) {
const lines = qif.split('\n').filter(Boolean);
let line = lines.shift();
const type = /!Type:([^$]*)$/.exec(line.trim());
const data: {
dateFormat: string | undefined;
type?;
transactions: QIFTransaction[];
} = {
dateFormat: options.dateFormat,
transactions: [],
};
const transactions = data.transactions;
let transaction: QIFTransaction = {};
if (!type || !type.length) {
throw new Error('File does not appear to be a valid qif file: ' + line);
}
data.type = type[1];
let division: Division = {};
while ((line = lines.shift())) {
line = line.trim();
if (line === '^') {
transactions.push(transaction);
transaction = {};
continue;
}
switch (line[0]) {
case 'D':
transaction.date = line.substring(1);
break;
case 'T':
transaction.amount = line.substring(1);View on GitHub (pinned to d4334cb6e6)