{"record":{"id":"0834168bbe9fd724","repo":"actualbudget/actual","slug":"invalid-month-format-use-yyyy-mm-month","errorCode":null,"errorMessage":"Invalid month format, use YYYY-MM: ${month}","messagePattern":"Invalid month format, use YYYY-MM: (.+?)","errorType":"exception","errorClass":"APIError","httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/api.ts","lineNumber":92,"sourceCode":"        if (connection.getNumClients() > 1) {\n          connection.send('sync-event', {\n            type: 'success',\n            tables: rows.map(row => row.dataset),\n          });\n        }\n\n        return result;\n      },\n      { undoDisabled: true },\n    );\n  };\n}\n\nlet handlers = {} as unknown as Handlers;\n\nasync function validateMonth(month) {\n  if (!month.match(/^\\d{4}-\\d{2}$/)) {\n    throw APIError('Invalid month format, use YYYY-MM: ' + month);\n  }\n\n  if (!IMPORT_MODE) {\n    const { start, end } = await handlers['get-budget-bounds']();\n    const range = monthUtils.range(start, end);\n    if (!range.includes(month)) {\n      throw APIError('No budget exists for month: ' + month);\n    }\n  }\n}\n\nasync function validateExpenseCategory(debug, id) {\n  if (id == null) {\n    throw APIError(`${debug}: category id is required`);\n  }\n\n  const row = await db.first<Pick<db.DbCategory, 'is_income'>>(\n    'SELECT is_income FROM categories WHERE id = ?',","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/api.ts#L74-L110","documentation":"validateMonth throws APIError when a month argument passed to a public API method does not match the required YYYY-MM format. The API validates all month-typed inputs before executing handlers so budget lookups receive a well-formed month string. This is an input validation guard, not a data problem.","triggerScenarios":"Calling API methods that take a month (e.g. getBudgetMonth, setBudgetAmount, getCategories) with strings like '2024', '2024-1', '2024/01', '2024-01-01', or an empty string.","commonSituations":"Passing a full ISO date ('2024-01-15') instead of a month; building the month string with JS Date without zero-padding; forgetting month is 1-indexed in some libraries (month '1' not '01'); passing undefined coerced to a string.","solutions":["Format the month as a 4-digit year, hyphen, 2-digit zero-padded month (e.g. '2024-01')","Use a formatter like month.toISOString().slice(0, 7) or date-fns format(date, 'yyyy-MM') to derive the string","Validate the string with /^\\d{4}-\\d{2}$/ before calling the API","If you have a full date, strip the day portion first"],"exampleFix":"// before\nawait q.getBudgetMonth('2024-1'); // APIError: Invalid month format\nawait q.getBudgetMonth(new Date().toISOString()); // '2024-01-15T...'\n// after\nawait q.getBudgetMonth('2024-01');\nawait q.getBudgetMonth(new Date().toISOString().slice(0, 7));","handlingStrategy":"validation","validationCode":"function isValidMonth(m) {\n  return typeof m === 'string' && /^\\d{4}-\\d{2}$/.test(m);\n}\nif (!isValidMonth(month)) throw new Error(`month must be YYYY-MM, got: ${month}`);","typeGuard":"function isMonth(value) {\n  return typeof value === 'string' && /^\\d{4}-\\d{2}$/.test(value);\n}","tryCatchPattern":null,"preventionTips":["Always derive month strings via toISOString().slice(0, 7) or date-fns format(date, 'yyyy-MM')","Never pass full ISO datetimes to month parameters","Zero-pad single-digit months","Add a regex assertion on month inputs in your own API wrapper"],"tags":["validation","api","input-format"],"backgroundTag":"invalid-argument-format","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}