{"record":{"id":"3166e68be9358782","repo":"actualbudget/actual","slug":"invalid-widget-idx-y-data-type-for-value-widg","errorCode":null,"errorMessage":"Invalid widget.${idx}.y data-type for value ${widget.y}.","messagePattern":"Invalid widget\\.(.+?)\\.y data-type for value (.+?)\\.","errorType":"validation","errorClass":"ValidationError","httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/dashboard/app.ts","lineNumber":79,"sourceCode":"\n    dashboard.widgets.forEach((widget, idx) => {\n      requiredFields(`Dashboard widget #${idx}`, widget, [\n        'type',\n        'x',\n        'y',\n        'width',\n        'height',\n        ...(isExportedCustomReportWidget(widget) ? ['meta' as const] : []),\n      ]);\n\n      if (!Number.isInteger(widget.x)) {\n        throw new ValidationError(\n          `Invalid widget.${idx}.x data-type for value ${widget.x}.`,\n        );\n      }\n\n      if (!Number.isInteger(widget.y)) {\n        throw new ValidationError(\n          `Invalid widget.${idx}.y data-type for value ${widget.y}.`,\n        );\n      }\n\n      if (!Number.isInteger(widget.width)) {\n        throw new ValidationError(\n          `Invalid widget.${idx}.width data-type for value ${widget.width}.`,\n        );\n      }\n\n      if (!Number.isInteger(widget.height)) {\n        throw new ValidationError(\n          `Invalid widget.${idx}.height data-type for value ${widget.height}.`,\n        );\n      }\n\n      if (!isWidgetType(widget.type)) {\n        throw new ValidationError(","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/dashboard/app.ts#L61-L97","documentation":"Each widget's y (grid row position) must be an integer. The dashboard import validator throws this ValidationError when widget.y is fractional, a string, null, or missing, since the grid layout requires whole-number rows.","triggerScenarios":"Importing dashboard JSON where a widget's y is 2.5, \"1\", null, or undefined; generated exports using float row math or omitting y entirely.","commonSituations":"Scripted dashboard generation stacking widgets with computed float offsets; JSON edited in a spreadsheet that emits string numerics; exports from tooling that forgot the y field.","solutions":["Set widget.y = Math.round(widget.y) before import.","Ensure every widget has an explicit integer y (e.g. sequential row indices).","Coerce strings with Number() and verify with Number.isInteger.","Fix the generator to compute rows with integer counters, not division results.","Re-export from the app for a valid baseline."],"exampleFix":"// before\nlet y = 0; for (const w of defs) { w.y = y; y += w.height / 2; }\n// after\nlet y = 0; for (const w of defs) { w.y = y; y += Math.ceil(w.height); }","handlingStrategy":"validation","validationCode":"widgets.forEach((w, i) => {\n  if (!Number.isInteger(w.y)) throw new Error(`Widget #${i}: y must be an integer, got ${w.y}`);\n});","typeGuard":"function hasIntegerRow(w: { y: unknown }): w is { y: number } {\n  return Number.isInteger(w.y);\n}","tryCatchPattern":"try {\n  await send('dashboard-import', { version: 1, widgets });\n} catch (e) {\n  if (e instanceof ValidationError && e.message.includes('.y data-type')) {\n    console.error('Non-integer y coordinate in import; round rows and retry');\n  } else throw e;\n}","preventionTips":["Compute row positions with integer counters, never division.","Pre-flight check every widget's y with Number.isInteger.","Assign sequential integer rows when generating layouts.","Re-export from the app if unsure of the correct schema."],"tags":["dashboard","validation","import","types"],"backgroundTag":"schema-validation-failed","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}