{"record":{"id":"f42bb2531e5af995","repo":"actualbudget/actual","slug":"unsupported-widget-type-widget-type","errorCode":null,"errorMessage":"Unsupported widget type: ${widget.type}","messagePattern":"Unsupported widget type: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/dashboard/app.ts","lineNumber":256,"sourceCode":"  );\n\n  if (!widget) {\n    throw new Error(`Widget not found: ${id}`);\n  }\n\n  await batchMessages(async () => {\n    // Insert the widget to target dashboard\n    if (isWidgetType(widget.type)) {\n      const newWidget = {\n        type: widget.type,\n        width: widget.width,\n        height: widget.height,\n        meta: widget.meta ? JSON.parse(widget.meta) : {},\n        dashboard_page_id: targetDashboardPageId,\n      };\n      await addDashboardWidget(newWidget);\n    } else {\n      throw new Error(`Unsupported widget type: ${widget.type}`);\n    }\n  });\n}\n\nasync function importDashboard({\n  filePath,\n  dashboardPageId,\n}: {\n  filePath: string;\n  dashboardPageId: string;\n}) {\n  try {\n    if (!(await fs.exists(filePath))) {\n      throw new Error(`File not found at the provided path: ${filePath}`);\n    }\n\n    const content = await fs.readFile(filePath);\n    const parsedContent: ExportImportDashboard = JSON.parse(content);","sourceCodeStart":238,"sourceCodeEnd":274,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/dashboard/app.ts#L238-L274","documentation":"Inside copyDashboardWidget, after loading the row, the code re-checks the widget type with isWidgetType before calling addDashboardWidget. Because the DB row's type is untyped, an unrecognized stored value reaches the else branch and throws `Unsupported widget type`. This is a defensive guard against corrupt or legacy rows.","triggerScenarios":"Copying a widget whose stored `type` column contains a value outside the supported list — e.g. data written by a newer version, manual DB edits, or corrupted rows.","commonSituations":"Downgraded Actual installs whose DB still holds widget types introduced later; rows tampered with via direct SQLite access; partially failed migrations.","solutions":["Fix the widget row's `type` in the dashboard table to a supported value (see isWidgetType).","Delete the invalid widget row (or tombstone it) and recreate the widget.","Upgrade Actual to a version that supports the stored widget type."],"exampleFix":"// before\nUPDATE dashboard SET type='future-card' WHERE id='...';\n// after\nUPDATE dashboard SET type='net-worth-card' WHERE id='...';","handlingStrategy":"type-guard","validationCode":"const SUPPORTED = ['net-worth-card','cash-flow-card','spending-card','crossover-card','budget-analysis-card','markdown-card','summary-card','calendar-card','formula-card','custom-report','sankey-card','balance-forecast-card','age-of-money-card','monte-carlo-card'];\nconst widget = await db.first('SELECT type FROM dashboard WHERE id = ?', [id]);\nif (widget && !SUPPORTED.includes(widget.type)) {\n  throw new Error(`Widget ${id} has unsupported stored type '${widget.type}'`);\n}","typeGuard":"function isWidgetType(type: string): type is DashboardWidgetEntity['type'] {\n  return ['net-worth-card','cash-flow-card','spending-card','crossover-card','budget-analysis-card','markdown-card','summary-card','calendar-card','formula-card','custom-report','sankey-card','balance-forecast-card','age-of-money-card','monte-carlo-card'].includes(type);\n}","tryCatchPattern":"try {\n  await send('dashboard-copy-widget', { id, targetDashboardPageId });\n} catch (e) {\n  if (e.message.startsWith('Unsupported widget type:')) {\n    console.error('DB contains a widget type this build cannot render; upgrade Actual or fix the row.');\n  }\n}","preventionTips":["Don't downgrade Actual below the version that wrote your dashboard widgets.","Avoid editing the dashboard table directly with SQLite tools.","Tombstone invalid widget rows after migrations instead of copying them."],"tags":["dashboard","data-integrity","guard"],"backgroundTag":"unsupported-enum-value","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}