{"record":{"id":"c7de6f67b055188a","repo":"actualbudget/actual","slug":"invalid-widget-idx-x-data-type-for-value-widg","errorCode":null,"errorMessage":"Invalid widget.${idx}.x data-type for value ${widget.x}.","messagePattern":"Invalid widget\\.(.+?)\\.x data-type for value (.+?)\\.","errorType":"validation","errorClass":"ValidationError","httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/dashboard/app.ts","lineNumber":73,"sourceCode":"\n    if (!Array.isArray(dashboard.widgets)) {\n      throw new ValidationError(\n        'Invalid dashboard.widgets data type: it must be an array of widgets.',\n      );\n    }\n\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(","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/dashboard/app.ts#L55-L91","documentation":"Each widget's x (grid column position) must be an integer. The dashboard import validator throws this ValidationError when widget.x is a float, string, null, or undefined, because grid coordinates must be whole numbers for the layout engine.","triggerScenarios":"Importing dashboard JSON where a widget's x is e.g. 1.5, \"0\", null, or the field is absent; hand-editing or programmatically generating the export with non-integer coordinates; parsing coordinates from float math elsewhere.","commonSituations":"Layouts computed from percentage math producing fractional x values; JSON round-trips through tools that stringify numbers; hand-crafted widget definitions missing x entirely.","solutions":["Round or floor the x value: widget.x = Math.round(widget.x).","Ensure every widget object includes a numeric integer x before import.","Convert string numbers: widget.x = Number(widget.x) then check Number.isInteger.","Fix the generating script so grid positions come from Math.floor of computed values.","Re-export from the app to get properly typed coordinates."],"exampleFix":"// before\nwidgets.push({ type: 'net', x: pos * 1.5, y: 0, width: 1, height: 1 });\n// after\nwidgets.push({ type: 'net', x: Math.round(pos * 1.5), y: 0, width: 1, height: 1 });","handlingStrategy":"validation","validationCode":"widgets.forEach((w, i) => {\n  if (!Number.isInteger(w.x)) throw new Error(`Widget #${i}: x must be an integer, got ${w.x}`);\n});","typeGuard":"function hasIntegerCoord(w: { x: unknown }): w is { x: number } {\n  return Number.isInteger(w.x);\n}","tryCatchPattern":"try {\n  await send('dashboard-import', { version: 1, widgets });\n} catch (e) {\n  if (e instanceof ValidationError && e.message.includes('.x data-type')) {\n    console.error('Non-integer x coordinate in import; round coordinates and retry');\n  } else throw e;\n}","preventionTips":["Always use Math.round/Math.floor for computed grid positions.","Validate the full export object before import.","Avoid JSON round-trips through tools that stringify numbers.","Write widget coordinates with integer counters, not division results."],"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"}