{"record":{"id":"f5192987e19af70f","repo":"actualbudget/actual","slug":"invalid-budget-id-id-check-the-id-of-your-bu","errorCode":null,"errorMessage":"Invalid budget id \"${id}\". Check the id of your budget in the Advanced section of the settings page.","messagePattern":"Invalid budget id \"(.+?)\"\\. Check the id of your budget in the Advanced section of the settings page\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/platform/server/fs/shared.ts","lineNumber":27,"sourceCode":"    throw new Error('Document directory is not set');\n  }\n  return documentDir;\n};\n\nexport const getBudgetDir = id => {\n  if (!id) {\n    throw new Error('getDocumentDir: id is falsy: ' + id);\n  }\n\n  // TODO: This should be better\n  //\n  // A cheesy safe guard. The id is generated from the budget name,\n  // so it provides an entry point for the user to accidentally (or\n  // intentionally) access other parts of the system. Always\n  // restrict it to only access files within the budget directory by\n  // never allowing slashes.\n  if (id.match(/[^A-Za-z0-9\\-_]/)) {\n    throw new Error(\n      `Invalid budget id \"${id}\". Check the id of your budget in the Advanced section of the settings page.`,\n    );\n  }\n\n  return join(getDocumentDir(), id);\n};\n","sourceCodeStart":9,"sourceCodeEnd":34,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/platform/server/fs/shared.ts#L9-L34","documentation":"Budget ids become a directory name under the document dir, so getBudgetDir sanitizes them: any character outside [A-Za-z0-9-_] (slashes, dots, spaces, unicode) is rejected to prevent path traversal outside the budget directory. The error tells the user to verify the budget id configured in the Advanced settings section.","triggerScenarios":"Passing a budget id containing '/', '\\', '..', whitespace, or other special characters to getBudgetDir; a hand-edited budget id in config/settings; constructing ids from user input without sanitization.","commonSituations":"A user pasted an incorrect or corrupted id into the Advanced > budget id setting; a self-hosted setup script generated an id from a budget name containing slashes; automated tooling injected URL-encoded or path-like ids.","solutions":["Open Settings > Advanced, check the budget id, and correct it to contain only letters, digits, dashes, or underscores.","Sanitize/validate ids on input: reject or transform anything not matching /^[A-Za-z0-9-_]+$/.","If an id was derived from a budget name, slugify it (strip or replace disallowed characters) before use."],"exampleFix":"// before\nconst id = budgetName; // e.g. 'My Budget/2024'\nconst dir = getBudgetDir(id);\n\n// after\nconst id = budgetName.replace(/[^A-Za-z0-9\\-_]/g, '-');\nif (!/^[A-Za-z0-9\\-_]+$/.test(id)) {\n  throw new Error('Budget id must be alphanumeric, dash, or underscore');\n}\nconst dir = getBudgetDir(id);","handlingStrategy":"validation","validationCode":"const SAFE_ID = /^[A-Za-z0-9\\-_]+$/;\nif (!SAFE_ID.test(budgetId)) {\n  throw new Error(`Budget id \"${budgetId}\" contains invalid characters`);\n}","typeGuard":"function isValidBudgetId(id: string): boolean {\n  return /^[A-Za-z0-9\\-_]+$/.test(id);\n}","tryCatchPattern":"try {\n  const dir = getBudgetDir(id);\n} catch (e) {\n  if (String(e.message).startsWith('Invalid budget id')) {\n    // surface settings-page guidance to the user\n  } else throw e;\n}","preventionTips":["Slugify ids derived from names before persisting them.","Never accept budget ids from unvalidated user input or URLs.","Document the allowed charset ([A-Za-z0-9-_]) wherever ids are configured."],"tags":["validation","path-traversal","security","filesystem"],"backgroundTag":"invalid-identifier","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}