{"record":{"id":"989e60f8b7490c2e","repo":"actualbudget/actual","slug":"error-reading-budget-yfull-file","errorCode":null,"errorMessage":"Error reading Budget.yfull file","messagePattern":"Error reading Budget\\.yfull file","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/importers/ynab4.ts","lineNumber":470,"sourceCode":"\n  const metaStr = Buffer.from(zipped[getFile(entries, root + 'Budget.ymeta')]);\n  const meta = JSON.parse(metaStr.toString('utf8'));\n  const budgetPath = join(root, meta.relativeDataFolderName);\n\n  const deviceFiles = entries.filter(e =>\n    e.startsWith(join(budgetPath, 'devices')),\n  );\n  const deviceGUID = findLatestDevice(zipped, deviceFiles);\n\n  const yfullPath = join(budgetPath, deviceGUID, 'Budget.yfull');\n  let contents;\n  try {\n    contents = Buffer.from(zipped[getFile(entries, yfullPath)]).toString(\n      'utf8',\n    );\n  } catch (e) {\n    logger.log(e);\n    throw new Error('Error reading Budget.yfull file');\n  }\n\n  try {\n    return JSON.parse(contents);\n  } catch {\n    throw new Error('Error parsing Budget.yfull file');\n  }\n}\n","sourceCodeStart":452,"sourceCodeEnd":479,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/importers/ynab4.ts#L452-L479","documentation":"After locating Budget.yfull inside the zip, parseFile converts the entry bytes to a UTF-8 string inside a try/catch and throws this error if reading fails — typically because getFile (290/291) or the buffer conversion failed, so the underlying cause is usually a missing/misplaced entry rather than an I/O problem.","triggerScenarios":"parseFile(buffer) where the Budget.yfull entry lookup throws (wrong path/case, duplicate entries) or the zipped entry is absent/undefined so Buffer.from fails to produce readable UTF-8 content.","commonSituations":"Re-zipped archives with renamed inner directories; partial uploads missing Budget.yfull; importing an archive whose root folder pattern /([^/]*\\.ynab4)/ does not match so the computed path is wrong.","solutions":["Check the import logs for the wrapped original error (e.g. 'Could not find file: ...') — fix the zip layout so Budget.yfull is at the expected path.","Re-export the budget from YNAB4 with the default folder structure and retry.","Confirm the archive actually contains Budget.yfull at its root folder (unzip -l).","If the file is present but binary-corrupted, restore from another backup."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"function zipContainsYfull(buffer) {\n  const entries = Object.keys(safeUnzip(buffer));\n  return entries.some(e => e.endsWith('Budget.yfull')) &&\n    entries.find(e => e.endsWith('Budget.yfull')).length > 0;\n}","typeGuard":"function isYnab4Layout(entries) {\n  return Array.isArray(entries) &&\n    entries.some(e => /[^/]*\\.ynab4\\//.test(e)) &&\n    entries.some(e => e.endsWith('Budget.yfull'));\n}","tryCatchPattern":"try {\n  const budget = parseFile(buffer);\n} catch (e) {\n  if (e.message === 'Error reading Budget.yfull file') {\n    // check logs for the wrapped cause (missing entry); fix zip layout\n  } else throw e;\n}","preventionTips":["Confirm Budget.yfull exists at the archive's root *.ynab4 folder before import.","Read the logger output for the underlying error — this message hides the real cause.","Restore missing files from another backup rather than hand-building zips.","Avoid renaming folders inside the export."],"tags":["import","ynab4","zip","file-not-found"],"backgroundTag":"import-file-not-found","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}