{"record":{"id":"4e85fde9bb7238f4","repo":"actualbudget/actual","slug":"unsupported-template-type-reached-migration","errorCode":null,"errorMessage":"Unsupported template type reached migration","messagePattern":"Unsupported template type reached migration","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/desktop-client/src/components/modals/BudgetAutomationsModal/migrateTemplatesToAutomations.ts","lineNumber":41,"sourceCode":"    case 'simple':\n      return 'fixed';\n    case 'limit':\n      return 'limit';\n    case 'refill':\n      return 'refill';\n    case 'average':\n    case 'copy':\n      return 'historical';\n    case 'by':\n    case 'spend':\n      return 'by';\n    case 'remainder':\n      return 'remainder';\n    case 'goal':\n      return 'goal';\n    case 'error':\n      // filtered upstream by hasUnsupportedDirective; surface if it ever isn't\n      throw new Error(`Unsupported template type reached migration`);\n    default: {\n      const _exhaustive: never = template;\n      void _exhaustive;\n      throw new Error(`Unhandled template type`);\n    }\n  }\n}\n\n// Fill in scheduleId from name (or refresh a stale name from scheduleId) so\n// the editor and engine consistently see both.\nfunction hydrateScheduleTemplate(\n  template: ScheduleTemplate,\n  schedules: readonly ScheduleEntity[],\n): ScheduleTemplate {\n  const schedule = template.scheduleId\n    ? schedules.find(s => s.id === template.scheduleId)\n    : template.name\n      ? schedules.find(s => s.name?.trim() === template.name?.trim())","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/desktop-client/src/components/modals/BudgetAutomationsModal/migrateTemplatesToAutomations.ts#L23-L59","documentation":"getDisplayTypeFromTemplate maps stored goal templates to editor display types during migration of templates to budget automations. A template with type 'error' means the goal template parser failed on that directive; these are supposed to be filtered out upstream by hasUnsupportedDirective before migration. This throw surfaces a bug where an error-typed (unparseable) template leaked into the migration path, indicating the upstream filter missed it.","triggerScenarios":"Running migrateTemplatesToAutomations on a budget whose template list still contains templates with `type: 'error'` because hasUnsupportedDirective did not exclude them — e.g. a directive that fails parsing into a supported automation, or a change in the parser producing error templates for previously valid directives.","commonSituations":"Budgets authored with goal templates that no longer parse after an upgrade; edits to template syntax (renamed/removed directive parameters) leaving error placeholders in notes; the upstream filter's predicate diverging from this switch's cases.","solutions":["Identify the offending template (its category/note text) and inspect why it parsed as type 'error'.","Fix or remove the malformed goal template directive in the category notes before migrating.","Extend hasUnsupportedDirective so error-typed templates are excluded from the migration input.","Update the parser/migration mapping if a previously supported directive now yields an error template after an upgrade."],"exampleFix":"// before\nconst toMigrate = templates.filter(t => t.directive === 'template');\n// after\nconst toMigrate = templates.filter(\n  t => t.directive === 'template' && !hasUnsupportedDirective(t),\n);","handlingStrategy":"validation","validationCode":"const migratable = templates.filter(\n  t => t.directive === 'template' && t.type !== 'error' && hasSupportedType(t),\n);\nmigrateTemplatesToAutomations(migratable, ...);","typeGuard":"function isMigratableTemplate(t: Template): boolean {\n  return t.type !== 'error' && MIGRATION_SUPPORTED_TYPES.has(t.type);\n}","tryCatchPattern":"try {\n  migrateTemplatesToAutomations(templates, schedules);\n} catch (err) {\n  if (String(err).includes('Unsupported template type reached migration')) {\n    notifyUser('Some goal templates could not be migrated and were skipped.');\n    return;\n  }\n  throw err;\n}","preventionTips":["Always run hasUnsupportedDirective over the template list before migration.","Fix or drop templates with type 'error' (unparseable directives) instead of migrating them.","After parser changes, re-validate previously stored goal templates.","Report skipped templates to the user rather than failing the whole migration."],"tags":["budget-templates","migration","automations","parsing"],"backgroundTag":"unsupported-template-type","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}